2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <boost/lexical_cast.hpp>
31 #include "pbd/convert.h"
32 #include "pbd/failed_constructor.h"
33 #include "pbd/stateful_diff_command.h"
34 #include "pbd/xml++.h"
36 #include "ardour/debug.h"
37 #include "ardour/playlist.h"
38 #include "ardour/session.h"
39 #include "ardour/region.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/region_sorters.h"
42 #include "ardour/playlist_factory.h"
43 #include "ardour/playlist_source.h"
44 #include "ardour/transient_detector.h"
45 #include "ardour/session_playlists.h"
46 #include "ardour/source_factory.h"
51 using namespace ARDOUR;
55 namespace Properties {
56 PBD::PropertyDescriptor<bool> regions;
60 struct ShowMeTheList {
61 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
63 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
65 boost::shared_ptr<Playlist> playlist;
72 Playlist::make_property_quarks ()
74 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
75 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
76 Properties::regions.property_id));
79 RegionListProperty::RegionListProperty (Playlist& pl)
80 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
86 RegionListProperty::RegionListProperty (RegionListProperty const & p)
87 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
88 , _playlist (p._playlist)
94 RegionListProperty::clone () const
96 return new RegionListProperty (*this);
100 RegionListProperty::create () const
102 return new RegionListProperty (_playlist);
106 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
108 /* All regions (even those which are deleted) have their state saved by other
109 code, so we can just store ID here.
112 node.add_property ("id", region->id().to_s ());
115 boost::shared_ptr<Region>
116 RegionListProperty::get_content_from_xml (XMLNode const & node) const
118 XMLProperty const * prop = node.property ("id");
121 PBD::ID id (prop->value ());
123 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
126 ret = RegionFactory::region_by_id (id);
132 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
133 : SessionObject(sess, nom)
138 first_set_state = false;
143 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
144 : SessionObject(sess, "unnamed playlist")
149 const XMLProperty* prop = node.property("type");
150 assert(!prop || DataType(prop->value()) == _type);
154 _name = "unnamed"; /* reset by set_state */
157 /* set state called by derived class */
160 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
161 : SessionObject(other->_session, namestr)
163 , _type(other->_type)
164 , _orig_diskstream_id (other->_orig_diskstream_id)
169 other->copy_regions (tmp);
173 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
174 add_region_internal( (*x), (*x)->position());
179 _splicing = other->_splicing;
180 _nudging = other->_nudging;
181 _edit_mode = other->_edit_mode;
184 first_set_state = false;
186 in_partition = false;
188 _read_data_count = 0;
189 _frozen = other->_frozen;
191 layer_op_counter = other->layer_op_counter;
192 freeze_length = other->freeze_length;
195 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
196 : SessionObject(other->_session, str)
198 , _type(other->_type)
199 , _orig_diskstream_id (other->_orig_diskstream_id)
201 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
203 framepos_t end = start + cnt - 1;
209 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
211 boost::shared_ptr<Region> region;
212 boost::shared_ptr<Region> new_region;
213 frameoffset_t offset = 0;
214 framepos_t position = 0;
221 overlap = region->coverage (start, end);
227 case OverlapInternal:
228 offset = start - region->position();
235 position = region->position() - start;
236 len = end - region->position();
240 offset = start - region->position();
242 len = region->length() - offset;
245 case OverlapExternal:
247 position = region->position() - start;
248 len = region->length();
252 RegionFactory::region_name (new_name, region->name(), false);
256 plist.add (Properties::start, region->start() + offset);
257 plist.add (Properties::length, len);
258 plist.add (Properties::name, new_name);
259 plist.add (Properties::layer, region->layer());
261 new_region = RegionFactory::RegionFactory::create (region, plist);
263 add_region_internal (new_region, position);
267 first_set_state = false;
274 InUse (true); /* EMIT SIGNAL */
285 InUse (false); /* EMIT SIGNAL */
290 Playlist::copy_regions (RegionList& newlist) const
292 RegionLock rlock (const_cast<Playlist *> (this));
294 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
295 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
300 Playlist::init (bool hide)
302 add_property (regions);
303 _xml_node_name = X_("Playlist");
305 g_atomic_int_set (&block_notifications, 0);
306 g_atomic_int_set (&ignore_state_changes, 0);
307 pending_contents_change = false;
308 pending_length = false;
309 pending_layering = false;
310 first_set_state = true;
318 _edit_mode = Config->get_edit_mode();
320 in_partition = false;
322 _read_data_count = 0;
324 layer_op_counter = 0;
326 _explicit_relayering = false;
329 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
330 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
332 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
335 Playlist::~Playlist ()
337 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
340 RegionLock rl (this);
342 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
343 (*i)->set_playlist (boost::shared_ptr<Playlist>());
347 /* GoingAway must be emitted by derived classes */
351 Playlist::_set_sort_id ()
354 Playlists are given names like <track name>.<id>
355 or <track name>.<edit group name>.<id> where id
356 is an integer. We extract the id and sort by that.
359 size_t dot_position = _name.val().find_last_of(".");
361 if (dot_position == string::npos) {
364 string t = _name.val().substr(dot_position + 1);
367 _sort_id = boost::lexical_cast<int>(t);
370 catch (boost::bad_lexical_cast e) {
377 Playlist::set_name (const string& str)
379 /* in a typical situation, a playlist is being used
380 by one diskstream and also is referenced by the
381 Session. if there are more references than that,
382 then don't change the name.
389 bool ret = SessionObject::set_name(str);
396 /***********************************************************************
397 CHANGE NOTIFICATION HANDLING
399 Notifications must be delayed till the region_lock is released. This
400 is necessary because handlers for the signals may need to acquire
401 the lock (e.g. to read from the playlist).
402 ***********************************************************************/
405 Playlist::begin_undo ()
412 Playlist::end_undo ()
421 delay_notifications ();
422 g_atomic_int_inc (&ignore_state_changes);
425 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
427 Playlist::thaw (bool from_undo)
429 g_atomic_int_dec_and_test (&ignore_state_changes);
430 release_notifications (from_undo);
435 Playlist::delay_notifications ()
437 g_atomic_int_inc (&block_notifications);
438 freeze_length = _get_extent().second;
441 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
443 Playlist::release_notifications (bool from_undo)
445 if (g_atomic_int_dec_and_test (&block_notifications)) {
446 flush_notifications (from_undo);
451 Playlist::notify_contents_changed ()
453 if (holding_state ()) {
454 pending_contents_change = true;
456 pending_contents_change = false;
457 ContentsChanged(); /* EMIT SIGNAL */
462 Playlist::notify_layering_changed ()
464 if (holding_state ()) {
465 pending_layering = true;
467 pending_layering = false;
468 LayeringChanged(); /* EMIT SIGNAL */
473 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
475 if (holding_state ()) {
476 pending_removes.insert (r);
477 pending_contents_change = true;
478 pending_length = true;
480 /* this might not be true, but we have to act
481 as though it could be.
483 pending_length = false;
484 LengthChanged (); /* EMIT SIGNAL */
485 pending_contents_change = false;
486 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
487 ContentsChanged (); /* EMIT SIGNAL */
492 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
494 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
496 if (holding_state ()) {
498 pending_range_moves.push_back (move);
502 list< Evoral::RangeMove<framepos_t> > m;
504 RangesMoved (m, false);
510 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
512 if (r->position() >= r->last_position()) {
513 /* trimmed shorter */
517 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
519 if (holding_state ()) {
521 pending_region_extensions.push_back (extra);
525 list<Evoral::Range<framepos_t> > r;
533 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
535 if (r->length() < r->last_length()) {
536 /* trimmed shorter */
539 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
541 if (holding_state ()) {
543 pending_region_extensions.push_back (extra);
547 list<Evoral::Range<framepos_t> > r;
555 Playlist::notify_region_added (boost::shared_ptr<Region> r)
557 /* the length change might not be true, but we have to act
558 as though it could be.
561 if (holding_state()) {
562 pending_adds.insert (r);
563 pending_contents_change = true;
564 pending_length = true;
567 pending_length = false;
568 LengthChanged (); /* EMIT SIGNAL */
569 pending_contents_change = false;
570 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
571 ContentsChanged (); /* EMIT SIGNAL */
576 Playlist::notify_length_changed ()
578 if (holding_state ()) {
579 pending_length = true;
581 pending_length = false;
582 LengthChanged(); /* EMIT SIGNAL */
583 pending_contents_change = false;
584 ContentsChanged (); /* EMIT SIGNAL */
588 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
590 Playlist::flush_notifications (bool from_undo)
592 set<boost::shared_ptr<Region> > dependent_checks_needed;
593 set<boost::shared_ptr<Region> >::iterator s;
594 uint32_t regions_changed = false;
595 bool check_length = false;
596 framecnt_t old_length = 0;
604 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
605 regions_changed = true;
606 if (!pending_length) {
607 old_length = _get_extent ().second;
612 /* we have no idea what order the regions ended up in pending
613 bounds (it could be based on selection order, for example).
614 so, to preserve layering in the "most recently moved is higher"
615 model, sort them by existing layer, then timestamp them.
618 // RegionSortByLayer cmp;
619 // pending_bounds.sort (cmp);
621 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
622 if (_session.config.get_layer_model() == MoveAddHigher) {
623 timestamp_layer_op (*r);
625 dependent_checks_needed.insert (*r);
628 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
629 remove_dependents (*s);
630 // cerr << _name << " sends RegionRemoved\n";
631 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
634 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
635 // cerr << _name << " sends RegionAdded\n";
636 /* don't emit RegionAdded signal until relayering is done,
637 so that the region is fully setup by the time
638 anyone hear's that its been added
640 dependent_checks_needed.insert (*s);
644 if (old_length != _get_extent().second) {
645 pending_length = true;
646 // cerr << _name << " length has changed\n";
650 if (pending_length || (freeze_length != _get_extent().second)) {
651 pending_length = false;
652 // cerr << _name << " sends LengthChanged\n";
653 LengthChanged(); /* EMIT SIGNAL */
656 if (regions_changed || pending_contents_change) {
660 pending_contents_change = false;
661 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
662 ContentsChanged (); /* EMIT SIGNAL */
663 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
666 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
667 (*s)->clear_changes ();
668 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
671 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
672 check_dependents (*s, false);
675 if (!pending_range_moves.empty ()) {
676 RangesMoved (pending_range_moves, from_undo);
679 if (!pending_region_extensions.empty ()) {
680 RegionsExtended (pending_region_extensions);
689 Playlist::clear_pending ()
691 pending_adds.clear ();
692 pending_removes.clear ();
693 pending_bounds.clear ();
694 pending_range_moves.clear ();
695 pending_region_extensions.clear ();
696 pending_contents_change = false;
697 pending_length = false;
700 /*************************************************************
702 *************************************************************/
705 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
707 RegionLock rlock (this);
708 times = fabs (times);
710 int itimes = (int) floor (times);
712 framepos_t pos = position;
714 if (times == 1 && auto_partition){
715 partition(pos - 1, (pos + region->length()), true);
719 add_region_internal (region, pos);
720 pos += region->length();
725 /* note that itimes can be zero if we being asked to just
726 insert a single fraction of the region.
729 for (int i = 0; i < itimes; ++i) {
730 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
731 add_region_internal (copy, pos);
732 pos += region->length();
735 framecnt_t length = 0;
737 if (floor (times) != times) {
738 length = (framecnt_t) floor (region->length() * (times - floor (times)));
740 RegionFactory::region_name (name, region->name(), false);
745 plist.add (Properties::start, region->start());
746 plist.add (Properties::length, length);
747 plist.add (Properties::name, name);
748 plist.add (Properties::layer, region->layer());
750 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
751 add_region_internal (sub, pos);
755 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
759 Playlist::set_region_ownership ()
761 RegionLock rl (this);
762 RegionList::iterator i;
763 boost::weak_ptr<Playlist> pl (shared_from_this());
765 for (i = regions.begin(); i != regions.end(); ++i) {
766 (*i)->set_playlist (pl);
771 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
773 if (region->data_type() != _type){
777 RegionSortByPosition cmp;
779 framecnt_t old_length = 0;
781 if (!holding_state()) {
782 old_length = _get_extent().second;
785 if (!first_set_state) {
786 boost::shared_ptr<Playlist> foo (shared_from_this());
787 region->set_playlist (boost::weak_ptr<Playlist>(foo));
790 region->set_position (position);
792 timestamp_layer_op (region);
794 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
795 all_regions.insert (region);
797 possibly_splice_unlocked (position, region->length(), region);
799 if (!holding_state ()) {
800 /* layers get assigned from XML state, and are not reset during undo/redo */
804 /* we need to notify the existence of new region before checking dependents. Ick. */
806 notify_region_added (region);
809 if (!holding_state ()) {
811 check_dependents (region, false);
813 if (old_length != _get_extent().second) {
814 notify_length_changed ();
818 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
824 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
826 RegionLock rlock (this);
828 bool old_sp = _splicing;
831 remove_region_internal (old);
832 add_region_internal (newr, pos);
836 possibly_splice_unlocked (pos, old->length() - newr->length());
840 Playlist::remove_region (boost::shared_ptr<Region> region)
842 RegionLock rlock (this);
843 remove_region_internal (region);
847 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
849 RegionList::iterator i;
850 framecnt_t old_length = 0;
852 if (!holding_state()) {
853 old_length = _get_extent().second;
858 region->set_playlist (boost::weak_ptr<Playlist>());
861 /* XXX should probably freeze here .... */
863 for (i = regions.begin(); i != regions.end(); ++i) {
866 framepos_t pos = (*i)->position();
867 framecnt_t distance = (*i)->length();
871 possibly_splice_unlocked (pos, -distance);
873 if (!holding_state ()) {
875 remove_dependents (region);
877 if (old_length != _get_extent().second) {
878 notify_length_changed ();
882 notify_region_removed (region);
891 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
893 if (Config->get_use_overlap_equivalency()) {
894 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
895 if ((*i)->overlap_equivalent (other)) {
896 results.push_back ((*i));
900 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
901 if ((*i)->equivalent (other)) {
902 results.push_back ((*i));
909 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
911 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
913 if ((*i) && (*i)->region_list_equivalent (other)) {
914 results.push_back (*i);
920 Playlist::partition (framepos_t start, framepos_t end, bool cut)
924 partition_internal (start, end, cut, thawlist);
926 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
927 (*i)->resume_property_changes ();
932 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
934 RegionList new_regions;
937 RegionLock rlock (this);
939 boost::shared_ptr<Region> region;
940 boost::shared_ptr<Region> current;
942 RegionList::iterator tmp;
944 framepos_t pos1, pos2, pos3, pos4;
948 /* need to work from a copy, because otherwise the regions we add during the process
949 get operated on as well.
952 RegionList copy = regions.rlist();
954 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
961 if (current->first_frame() >= start && current->last_frame() < end) {
964 remove_region_internal (current);
970 /* coverage will return OverlapStart if the start coincides
971 with the end point. we do not partition such a region,
972 so catch this special case.
975 if (current->first_frame() >= end) {
979 if ((overlap = current->coverage (start, end)) == OverlapNone) {
983 pos1 = current->position();
986 pos4 = current->last_frame();
988 if (overlap == OverlapInternal) {
989 /* split: we need 3 new regions, the front, middle and end.
990 cut: we need 2 regions, the front and end.
995 ---------------*************************------------
998 ---------------*****++++++++++++++++====------------
1000 ---------------*****----------------====------------
1005 /* "middle" ++++++ */
1007 RegionFactory::region_name (new_name, current->name(), false);
1011 plist.add (Properties::start, current->start() + (pos2 - pos1));
1012 plist.add (Properties::length, pos3 - pos2);
1013 plist.add (Properties::name, new_name);
1014 plist.add (Properties::layer, regions.size());
1015 plist.add (Properties::automatic, true);
1016 plist.add (Properties::left_of_split, true);
1017 plist.add (Properties::right_of_split, true);
1019 region = RegionFactory::create (current, plist);
1020 add_region_internal (region, start);
1021 new_regions.push_back (region);
1026 RegionFactory::region_name (new_name, current->name(), false);
1030 plist.add (Properties::start, current->start() + (pos3 - pos1));
1031 plist.add (Properties::length, pos4 - pos3);
1032 plist.add (Properties::name, new_name);
1033 plist.add (Properties::layer, regions.size());
1034 plist.add (Properties::automatic, true);
1035 plist.add (Properties::right_of_split, true);
1037 region = RegionFactory::create (current, plist);
1039 add_region_internal (region, end);
1040 new_regions.push_back (region);
1044 current->suspend_property_changes ();
1045 thawlist.push_back (current);
1046 current->cut_end (pos2 - 1);
1048 } else if (overlap == OverlapEnd) {
1052 ---------------*************************------------
1055 ---------------**************+++++++++++------------
1057 ---------------**************-----------------------
1064 RegionFactory::region_name (new_name, current->name(), false);
1068 plist.add (Properties::start, current->start() + (pos2 - pos1));
1069 plist.add (Properties::length, pos4 - pos2);
1070 plist.add (Properties::name, new_name);
1071 plist.add (Properties::layer, regions.size());
1072 plist.add (Properties::automatic, true);
1073 plist.add (Properties::left_of_split, true);
1075 region = RegionFactory::create (current, plist);
1077 add_region_internal (region, start);
1078 new_regions.push_back (region);
1083 current->suspend_property_changes ();
1084 thawlist.push_back (current);
1085 current->cut_end (pos2 - 1);
1087 } else if (overlap == OverlapStart) {
1089 /* split: we need 2 regions: the front and the end.
1090 cut: just trim current to skip the cut area
1095 ---------------*************************------------
1099 ---------------****+++++++++++++++++++++------------
1101 -------------------*********************------------
1107 RegionFactory::region_name (new_name, current->name(), false);
1111 plist.add (Properties::start, current->start());
1112 plist.add (Properties::length, pos3 - pos1);
1113 plist.add (Properties::name, new_name);
1114 plist.add (Properties::layer, regions.size());
1115 plist.add (Properties::automatic, true);
1116 plist.add (Properties::right_of_split, true);
1118 region = RegionFactory::create (current, plist);
1120 add_region_internal (region, pos1);
1121 new_regions.push_back (region);
1126 current->suspend_property_changes ();
1127 thawlist.push_back (current);
1128 current->trim_front (pos3);
1129 } else if (overlap == OverlapExternal) {
1131 /* split: no split required.
1132 cut: remove the region.
1137 ---------------*************************------------
1141 ---------------*************************------------
1143 ----------------------------------------------------
1148 remove_region_internal (current);
1151 new_regions.push_back (current);
1155 in_partition = false;
1158 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1159 check_dependents (*i, false);
1163 boost::shared_ptr<Playlist>
1164 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1166 boost::shared_ptr<Playlist> ret;
1167 boost::shared_ptr<Playlist> pl;
1170 if (ranges.empty()) {
1171 return boost::shared_ptr<Playlist>();
1174 start = ranges.front().start;
1176 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1178 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1180 if (i == ranges.begin()) {
1184 /* paste the next section into the nascent playlist,
1185 offset to reflect the start of the first range we
1189 ret->paste (pl, (*i).start - start, 1.0f);
1196 boost::shared_ptr<Playlist>
1197 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1199 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1200 return cut_copy (pmf, ranges, result_is_hidden);
1203 boost::shared_ptr<Playlist>
1204 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1206 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1207 return cut_copy (pmf, ranges, result_is_hidden);
1210 boost::shared_ptr<Playlist>
1211 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1213 boost::shared_ptr<Playlist> the_copy;
1214 RegionList thawlist;
1217 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1218 string new_name = _name;
1222 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1223 return boost::shared_ptr<Playlist>();
1226 partition_internal (start, start+cnt-1, true, thawlist);
1228 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1229 (*i)->resume_property_changes();
1235 boost::shared_ptr<Playlist>
1236 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1240 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1241 string new_name = _name;
1245 cnt = min (_get_extent().second - start, cnt);
1246 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1250 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1252 times = fabs (times);
1255 RegionLock rl1 (this);
1256 RegionLock rl2 (other.get());
1258 framecnt_t const old_length = _get_extent().second;
1260 int itimes = (int) floor (times);
1261 framepos_t pos = position;
1262 framecnt_t const shift = other->_get_extent().second;
1263 layer_t top_layer = regions.size();
1266 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1267 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1269 /* put these new regions on top of all existing ones, but preserve
1270 the ordering they had in the original playlist.
1273 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1274 add_region_internal (copy_of_region, (*i)->position() + pos);
1280 /* XXX shall we handle fractional cases at some point? */
1282 if (old_length != _get_extent().second) {
1283 notify_length_changed ();
1294 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1296 times = fabs (times);
1298 RegionLock rl (this);
1299 int itimes = (int) floor (times);
1300 framepos_t pos = position + 1;
1303 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1304 add_region_internal (copy, pos);
1305 pos += region->length();
1308 if (floor (times) != times) {
1309 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1311 RegionFactory::region_name (name, region->name(), false);
1316 plist.add (Properties::start, region->start());
1317 plist.add (Properties::length, length);
1318 plist.add (Properties::name, name);
1320 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1321 add_region_internal (sub, pos);
1327 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1329 RegionLock rlock (this);
1330 RegionList copy (regions.rlist());
1333 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1335 if ((*r)->last_frame() < at) {
1340 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1341 /* intersected region */
1342 if (!move_intersected) {
1347 /* do not move regions glued to music time - that
1348 has to be done separately.
1351 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1352 fixup.push_back (*r);
1356 (*r)->set_position ((*r)->position() + distance);
1359 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1360 (*r)->recompute_position_from_lock_style ();
1365 Playlist::split (framepos_t at)
1367 RegionLock rlock (this);
1368 RegionList copy (regions.rlist());
1370 /* use a copy since this operation can modify the region list
1373 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1374 _split_region (*r, at);
1379 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1381 RegionLock rl (this);
1382 _split_region (region, playlist_position);
1386 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1388 if (!region->covers (playlist_position)) {
1392 if (region->position() == playlist_position ||
1393 region->last_frame() == playlist_position) {
1397 boost::shared_ptr<Region> left;
1398 boost::shared_ptr<Region> right;
1399 frameoffset_t before;
1400 frameoffset_t after;
1404 /* split doesn't change anything about length, so don't try to splice */
1406 bool old_sp = _splicing;
1409 before = playlist_position - region->position();
1410 after = region->length() - before;
1412 RegionFactory::region_name (before_name, region->name(), false);
1417 plist.add (Properties::position, region->position ());
1418 plist.add (Properties::length, before);
1419 plist.add (Properties::name, before_name);
1420 plist.add (Properties::left_of_split, true);
1422 /* note: we must use the version of ::create with an offset here,
1423 since it supplies that offset to the Region constructor, which
1424 is necessary to get audio region gain envelopes right.
1426 left = RegionFactory::create (region, 0, plist);
1429 RegionFactory::region_name (after_name, region->name(), false);
1434 plist.add (Properties::position, region->position() + before);
1435 plist.add (Properties::length, after);
1436 plist.add (Properties::name, after_name);
1437 plist.add (Properties::right_of_split, true);
1439 /* same note as above */
1440 right = RegionFactory::create (region, before, plist);
1443 add_region_internal (left, region->position());
1444 add_region_internal (right, region->position() + before);
1446 uint64_t orig_layer_op = region->last_layer_op();
1447 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1448 if ((*i)->last_layer_op() > orig_layer_op) {
1449 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1453 left->set_last_layer_op ( orig_layer_op );
1454 right->set_last_layer_op ( orig_layer_op + 1);
1458 finalize_split_region (region, left, right);
1460 remove_region_internal (region);
1466 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1468 if (_splicing || in_set_state) {
1469 /* don't respond to splicing moves or state setting */
1473 if (_edit_mode == Splice) {
1474 splice_locked (at, distance, exclude);
1479 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1481 if (_splicing || in_set_state) {
1482 /* don't respond to splicing moves or state setting */
1486 if (_edit_mode == Splice) {
1487 splice_unlocked (at, distance, exclude);
1492 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1495 RegionLock rl (this);
1496 core_splice (at, distance, exclude);
1501 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1503 core_splice (at, distance, exclude);
1507 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1511 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1513 if (exclude && (*i) == exclude) {
1517 if ((*i)->position() >= at) {
1518 framepos_t new_pos = (*i)->position() + distance;
1521 } else if (new_pos >= max_framepos - (*i)->length()) {
1522 new_pos = max_framepos - (*i)->length();
1525 (*i)->set_position (new_pos);
1531 notify_length_changed ();
1535 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1537 if (in_set_state || _splicing || _nudging || _shuffling) {
1541 if (what_changed.contains (Properties::position)) {
1543 /* remove it from the list then add it back in
1544 the right place again.
1547 RegionSortByPosition cmp;
1549 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1551 if (i == regions.end()) {
1552 /* the region bounds are being modified but its not currently
1553 in the region list. we will use its bounds correctly when/if
1560 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1563 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1565 frameoffset_t delta = 0;
1567 if (what_changed.contains (Properties::position)) {
1568 delta = region->position() - region->last_position();
1571 if (what_changed.contains (Properties::length)) {
1572 delta += region->length() - region->last_length();
1576 possibly_splice (region->last_position() + region->last_length(), delta, region);
1579 if (holding_state ()) {
1580 pending_bounds.push_back (region);
1582 if (_session.config.get_layer_model() == MoveAddHigher) {
1583 /* it moved or changed length, so change the timestamp */
1584 timestamp_layer_op (region);
1587 notify_length_changed ();
1589 check_dependents (region, false);
1595 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1597 boost::shared_ptr<Region> region (weak_region.lock());
1603 /* this makes a virtual call to the right kind of playlist ... */
1605 region_changed (what_changed, region);
1609 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1611 PropertyChange our_interests;
1612 PropertyChange bounds;
1613 PropertyChange pos_and_length;
1616 if (in_set_state || in_flush) {
1620 our_interests.add (Properties::muted);
1621 our_interests.add (Properties::layer);
1622 our_interests.add (Properties::opaque);
1624 bounds.add (Properties::start);
1625 bounds.add (Properties::position);
1626 bounds.add (Properties::length);
1628 pos_and_length.add (Properties::position);
1629 pos_and_length.add (Properties::length);
1631 if (what_changed.contains (bounds)) {
1632 region_bounds_changed (what_changed, region);
1633 save = !(_splicing || _nudging);
1636 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1637 check_dependents (region, false);
1640 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1641 notify_region_moved (region);
1642 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1643 notify_region_end_trimmed (region);
1644 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1645 notify_region_start_trimmed (region);
1648 /* don't notify about layer changes, since we are the only object that can initiate
1649 them, and we notify in ::relayer()
1652 if (what_changed.contains (our_interests)) {
1660 Playlist::drop_regions ()
1662 RegionLock rl (this);
1664 all_regions.clear ();
1668 Playlist::sync_all_regions_with_regions ()
1670 RegionLock rl (this);
1672 all_regions.clear ();
1674 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1675 all_regions.insert (*i);
1680 Playlist::clear (bool with_signals)
1683 RegionLock rl (this);
1685 region_state_changed_connections.drop_connections ();
1687 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1688 pending_removes.insert (*i);
1693 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1694 remove_dependents (*s);
1700 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1701 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1704 pending_removes.clear ();
1705 pending_length = false;
1707 pending_contents_change = false;
1713 /***********************************************************************
1715 **********************************************************************/
1717 Playlist::RegionList *
1718 Playlist::regions_at (framepos_t frame)
1721 RegionLock rlock (this);
1722 return find_regions_at (frame);
1726 Playlist::count_regions_at (framepos_t frame) const
1728 RegionLock rlock (const_cast<Playlist*>(this));
1731 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1732 if ((*i)->covers (frame)) {
1740 boost::shared_ptr<Region>
1741 Playlist::top_region_at (framepos_t frame)
1744 RegionLock rlock (this);
1745 RegionList *rlist = find_regions_at (frame);
1746 boost::shared_ptr<Region> region;
1748 if (rlist->size()) {
1749 RegionSortByLayer cmp;
1751 region = rlist->back();
1758 boost::shared_ptr<Region>
1759 Playlist::top_unmuted_region_at (framepos_t frame)
1762 RegionLock rlock (this);
1763 RegionList *rlist = find_regions_at (frame);
1765 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1767 RegionList::iterator tmp = i;
1770 if ((*i)->muted()) {
1777 boost::shared_ptr<Region> region;
1779 if (rlist->size()) {
1780 RegionSortByLayer cmp;
1782 region = rlist->back();
1789 Playlist::RegionList*
1790 Playlist::regions_to_read (framepos_t start, framepos_t end)
1792 /* Caller must hold lock */
1794 RegionList covering;
1795 set<framepos_t> to_check;
1796 set<boost::shared_ptr<Region> > unique;
1798 to_check.insert (start);
1799 to_check.insert (end);
1801 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1803 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1805 /* find all/any regions that span start+end */
1807 switch ((*i)->coverage (start, end)) {
1811 case OverlapInternal:
1812 covering.push_back (*i);
1813 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1817 to_check.insert ((*i)->position());
1818 if ((*i)->position() != 0) {
1819 to_check.insert ((*i)->position()-1);
1821 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1822 covering.push_back (*i);
1826 to_check.insert ((*i)->last_frame());
1827 to_check.insert ((*i)->last_frame()+1);
1828 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1829 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1830 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1831 covering.push_back (*i);
1834 case OverlapExternal:
1835 covering.push_back (*i);
1836 to_check.insert ((*i)->position());
1837 if ((*i)->position() != 0) {
1838 to_check.insert ((*i)->position()-1);
1840 to_check.insert ((*i)->last_frame());
1841 to_check.insert ((*i)->last_frame()+1);
1842 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1843 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1844 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1848 /* don't go too far */
1850 if ((*i)->position() > end) {
1855 RegionList* rlist = new RegionList;
1857 /* find all the regions that cover each position .... */
1859 if (covering.size() == 1) {
1861 rlist->push_back (covering.front());
1862 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1867 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1871 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1873 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1875 if ((*x)->covers (*t)) {
1876 here.push_back (*x);
1877 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1881 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1888 RegionSortByLayer cmp;
1891 /* ... and get the top/transparent regions at "here" */
1893 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1897 if ((*c)->opaque()) {
1899 /* the other regions at this position are hidden by this one */
1900 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1907 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1908 rlist->push_back (*s);
1911 if (rlist->size() > 1) {
1912 /* now sort by time order */
1914 RegionSortByPosition cmp;
1919 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1924 Playlist::RegionList *
1925 Playlist::find_regions_at (framepos_t frame)
1927 /* Caller must hold lock */
1929 RegionList *rlist = new RegionList;
1931 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1932 if ((*i)->covers (frame)) {
1933 rlist->push_back (*i);
1940 Playlist::RegionList *
1941 Playlist::regions_touched (framepos_t start, framepos_t end)
1943 RegionLock rlock (this);
1944 RegionList *rlist = new RegionList;
1946 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1947 if ((*i)->coverage (start, end) != OverlapNone) {
1948 rlist->push_back (*i);
1956 Playlist::find_next_transient (framepos_t from, int dir)
1958 RegionLock rlock (this);
1959 AnalysisFeatureList points;
1960 AnalysisFeatureList these_points;
1962 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1964 if ((*i)->last_frame() < from) {
1968 if ((*i)->first_frame() > from) {
1973 (*i)->get_transients (these_points);
1975 /* add first frame, just, err, because */
1977 these_points.push_back ((*i)->first_frame());
1979 points.insert (points.end(), these_points.begin(), these_points.end());
1980 these_points.clear ();
1983 if (points.empty()) {
1987 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1988 bool reached = false;
1991 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1996 if (reached && (*x) > from) {
2001 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2006 if (reached && (*x) < from) {
2015 boost::shared_ptr<Region>
2016 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2018 RegionLock rlock (this);
2019 boost::shared_ptr<Region> ret;
2020 framepos_t closest = max_framepos;
2022 bool end_iter = false;
2024 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2028 frameoffset_t distance;
2029 boost::shared_ptr<Region> r = (*i);
2034 pos = r->first_frame ();
2037 pos = r->last_frame ();
2040 pos = r->sync_position ();
2045 case 1: /* forwards */
2048 if ((distance = pos - frame) < closest) {
2057 default: /* backwards */
2060 if ((distance = frame - pos) < closest) {
2077 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2079 RegionLock rlock (this);
2081 framepos_t closest = max_framepos;
2082 framepos_t ret = -1;
2086 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2088 boost::shared_ptr<Region> r = (*i);
2089 frameoffset_t distance;
2091 if (r->first_frame() > frame) {
2093 distance = r->first_frame() - frame;
2095 if (distance < closest) {
2096 ret = r->first_frame();
2101 if (r->last_frame () > frame) {
2103 distance = r->last_frame () - frame;
2105 if (distance < closest) {
2106 ret = r->last_frame ();
2114 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2116 boost::shared_ptr<Region> r = (*i);
2117 frameoffset_t distance;
2119 if (r->last_frame() < frame) {
2121 distance = frame - r->last_frame();
2123 if (distance < closest) {
2124 ret = r->last_frame();
2129 if (r->first_frame() < frame) {
2131 distance = frame - r->first_frame();
2133 if (distance < closest) {
2134 ret = r->first_frame();
2145 /***********************************************************************/
2151 Playlist::mark_session_dirty ()
2153 if (!in_set_state && !holding_state ()) {
2154 _session.set_dirty();
2159 Playlist::rdiff (vector<Command*>& cmds) const
2161 RegionLock rlock (const_cast<Playlist *> (this));
2162 Stateful::rdiff (cmds);
2166 Playlist::clear_owned_changes ()
2168 RegionLock rlock (this);
2169 Stateful::clear_owned_changes ();
2173 Playlist::update (const RegionListProperty::ChangeRecord& change)
2175 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2176 name(), change.added.size(), change.removed.size()));
2179 /* add the added regions */
2180 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2181 add_region ((*i), (*i)->position());
2183 /* remove the removed regions */
2184 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2192 Playlist::set_state (const XMLNode& node, int version)
2196 XMLNodeConstIterator niter;
2197 XMLPropertyList plist;
2198 XMLPropertyConstIterator piter;
2200 boost::shared_ptr<Region> region;
2202 bool seen_region_nodes = false;
2207 if (node.name() != "Playlist") {
2214 plist = node.properties();
2216 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2220 if (prop->name() == X_("name")) {
2221 _name = prop->value();
2223 } else if (prop->name() == X_("id")) {
2224 _id = prop->value();
2225 } else if (prop->name() == X_("orig-diskstream-id")) {
2226 _orig_diskstream_id = prop->value ();
2227 } else if (prop->name() == X_("frozen")) {
2228 _frozen = string_is_affirmative (prop->value());
2229 } else if (prop->name() == X_("combine-ops")) {
2230 _combine_ops = atoi (prop->value());
2236 nlist = node.children();
2238 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2242 if (child->name() == "Region") {
2244 seen_region_nodes = true;
2246 if ((prop = child->property ("id")) == 0) {
2247 error << _("region state node has no ID, ignored") << endmsg;
2251 ID id = prop->value ();
2253 if ((region = region_by_id (id))) {
2255 region->suspend_property_changes ();
2257 if (region->set_state (*child, version)) {
2258 region->resume_property_changes ();
2262 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2263 region->suspend_property_changes ();
2265 error << _("Playlist: cannot create region from XML") << endmsg;
2269 add_region (region, region->position(), 1.0);
2271 // So that layer_op ordering doesn't get screwed up
2272 region->set_last_layer_op( region->layer());
2273 region->resume_property_changes ();
2278 if (seen_region_nodes && regions.empty()) {
2282 /* update dependents, which was not done during add_region_internal
2283 due to in_set_state being true
2286 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2287 check_dependents (*r, false);
2292 notify_contents_changed ();
2295 first_set_state = false;
2300 Playlist::get_state()
2302 return state (true);
2306 Playlist::get_template()
2308 return state (false);
2311 /** @param full_state true to include regions in the returned state, otherwise false.
2314 Playlist::state (bool full_state)
2316 XMLNode *node = new XMLNode (X_("Playlist"));
2319 node->add_property (X_("id"), id().to_s());
2320 node->add_property (X_("name"), _name);
2321 node->add_property (X_("type"), _type.to_string());
2323 _orig_diskstream_id.print (buf, sizeof (buf));
2324 node->add_property (X_("orig-diskstream-id"), buf);
2325 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2328 RegionLock rlock (this, false);
2330 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2331 node->add_property ("combine-ops", buf);
2333 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2334 node->add_child_nocopy ((*i)->get_state());
2339 node->add_child_copy (*_extra_xml);
2346 Playlist::empty() const
2348 RegionLock rlock (const_cast<Playlist *>(this), false);
2349 return regions.empty();
2353 Playlist::n_regions() const
2355 RegionLock rlock (const_cast<Playlist *>(this), false);
2356 return regions.size();
2359 pair<framepos_t, framepos_t>
2360 Playlist::get_extent () const
2362 RegionLock rlock (const_cast<Playlist *>(this), false);
2363 return _get_extent ();
2366 pair<framepos_t, framepos_t>
2367 Playlist::_get_extent () const
2369 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2371 if (regions.empty()) {
2376 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2377 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2378 if (e.first < ext.first) {
2379 ext.first = e.first;
2381 if (e.second > ext.second) {
2382 ext.second = e.second;
2390 Playlist::bump_name (string name, Session &session)
2392 string newname = name;
2395 newname = bump_name_once (newname, '.');
2396 } while (session.playlists->by_name (newname)!=NULL);
2403 Playlist::top_layer() const
2405 RegionLock rlock (const_cast<Playlist *> (this));
2408 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2409 top = max (top, (*i)->layer());
2415 Playlist::set_edit_mode (EditMode mode)
2420 /********************
2422 ********************/
2425 Playlist::relayer ()
2427 /* never compute layers when changing state for undo/redo or setting from XML */
2429 if (in_update || in_set_state) {
2433 bool changed = false;
2435 /* Build up a new list of regions on each layer, stored in a set of lists
2436 each of which represent some period of time on some layer. The idea
2437 is to avoid having to search the entire region list to establish whether
2438 each region overlaps another */
2440 /* how many pieces to divide this playlist's time up into */
2441 int const divisions = 512;
2443 /* find the start and end positions of the regions on this playlist */
2444 framepos_t start = INT64_MAX;
2446 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2447 start = min (start, (*i)->position());
2448 end = max (end, (*i)->position() + (*i)->length());
2451 /* hence the size of each time division */
2452 double const division_size = (end - start) / double (divisions);
2454 vector<vector<RegionList> > layers;
2455 layers.push_back (vector<RegionList> (divisions));
2457 /* we want to go through regions from desired lowest to desired highest layer,
2458 which depends on the layer model
2461 RegionList copy = regions.rlist();
2463 /* sort according to the model and the layering mode that we're in */
2465 if (_explicit_relayering) {
2467 copy.sort (RegionSortByLayerWithPending ());
2469 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2471 copy.sort (RegionSortByLastLayerOp ());
2476 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2478 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2479 (*i)->set_pending_explicit_relayer (false);
2481 /* find the time divisions that this region covers; if there are no regions on the list,
2482 division_size will equal 0 and in this case we'll just say that
2483 start_division = end_division = 0.
2485 int start_division = 0;
2486 int end_division = 0;
2488 if (division_size > 0) {
2489 start_division = floor ( ((*i)->position() - start) / division_size);
2490 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2491 if (end_division == divisions) {
2496 assert (divisions == 0 || end_division < divisions);
2498 /* find the lowest layer that this region can go on */
2499 size_t j = layers.size();
2501 /* try layer j - 1; it can go on if it overlaps no other region
2502 that is already on that layer
2505 bool overlap = false;
2506 for (int k = start_division; k <= end_division; ++k) {
2507 RegionList::iterator l = layers[j-1][k].begin ();
2508 while (l != layers[j-1][k].end()) {
2509 if ((*l)->overlap_equivalent (*i)) {
2522 /* overlap, so we must use layer j */
2529 if (j == layers.size()) {
2530 /* we need a new layer for this region */
2531 layers.push_back (vector<RegionList> (divisions));
2534 /* put a reference to this region in each of the divisions that it exists in */
2535 for (int k = start_division; k <= end_division; ++k) {
2536 layers[j][k].push_back (*i);
2539 if ((*i)->layer() != j) {
2543 (*i)->set_layer (j);
2547 notify_layering_changed ();
2551 /* XXX these layer functions are all deprecated */
2554 Playlist::raise_region (boost::shared_ptr<Region> region)
2556 uint32_t top = regions.size() - 1;
2557 layer_t target = region->layer() + 1U;
2559 if (target >= top) {
2560 /* its already at the effective top */
2564 move_region_to_layer (target, region, 1);
2568 Playlist::lower_region (boost::shared_ptr<Region> region)
2570 if (region->layer() == 0) {
2571 /* its already at the bottom */
2575 layer_t target = region->layer() - 1U;
2577 move_region_to_layer (target, region, -1);
2581 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2583 /* does nothing useful if layering mode is later=higher */
2584 switch (_session.config.get_layer_model()) {
2591 layer_t top = regions.size() - 1;
2593 if (region->layer() >= top) {
2594 /* already on the top */
2598 move_region_to_layer (top, region, 1);
2599 /* mark the region's last_layer_op as now, so that it remains on top when
2600 doing future relayers (until something else takes over)
2602 timestamp_layer_op (region);
2606 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2608 /* does nothing useful if layering mode is later=higher */
2609 switch (_session.config.get_layer_model()) {
2616 if (region->layer() == 0) {
2617 /* already on the bottom */
2621 move_region_to_layer (0, region, -1);
2622 /* force region's last layer op to zero so that it stays at the bottom
2623 when doing future relayers
2625 region->set_last_layer_op (0);
2629 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2631 RegionList::iterator i;
2632 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2633 list<LayerInfo> layerinfo;
2636 RegionLock rlock (const_cast<Playlist *> (this));
2638 for (i = regions.begin(); i != regions.end(); ++i) {
2648 /* region is moving up, move all regions on intermediate layers
2652 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2653 dest = (*i)->layer() - 1;
2660 /* region is moving down, move all regions on intermediate layers
2664 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2665 dest = (*i)->layer() + 1;
2675 newpair.second = dest;
2677 layerinfo.push_back (newpair);
2683 /* now reset the layers without holding the region lock */
2685 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2686 x->first->set_layer (x->second);
2689 region->set_layer (target_layer);
2691 /* now check all dependents, since we changed the layering */
2693 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2694 check_dependents (x->first, false);
2697 check_dependents (region, false);
2698 notify_layering_changed ();
2706 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2708 RegionList::iterator i;
2714 RegionLock rlock (const_cast<Playlist *> (this));
2716 for (i = regions.begin(); i != regions.end(); ++i) {
2718 if ((*i)->position() >= start) {
2724 if ((*i)->last_frame() > max_framepos - distance) {
2725 new_pos = max_framepos - (*i)->length();
2727 new_pos = (*i)->position() + distance;
2732 if ((*i)->position() > distance) {
2733 new_pos = (*i)->position() - distance;
2739 (*i)->set_position (new_pos);
2747 notify_length_changed ();
2753 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2755 RegionLock rlock (const_cast<Playlist*> (this));
2757 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2758 if ((*r)->uses_source (src)) {
2766 boost::shared_ptr<Region>
2767 Playlist::find_region (const ID& id) const
2769 RegionLock rlock (const_cast<Playlist*> (this));
2771 /* searches all regions currently in use by the playlist */
2773 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2774 if ((*i)->id() == id) {
2779 return boost::shared_ptr<Region> ();
2783 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2785 RegionLock rlock (const_cast<Playlist*> (this));
2788 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2797 boost::shared_ptr<Region>
2798 Playlist::region_by_id (const ID& id) const
2800 /* searches all regions ever added to this playlist */
2802 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2803 if ((*i)->id() == id) {
2807 return boost::shared_ptr<Region> ();
2811 Playlist::dump () const
2813 boost::shared_ptr<Region> r;
2815 cerr << "Playlist \"" << _name << "\" " << endl
2816 << regions.size() << " regions "
2819 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2821 cerr << " " << r->name() << " ["
2822 << r->start() << "+" << r->length()
2832 Playlist::set_frozen (bool yn)
2838 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2840 region->set_last_layer_op (++layer_op_counter);
2845 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2849 if (region->locked()) {
2856 RegionLock rlock (const_cast<Playlist*> (this));
2861 RegionList::iterator next;
2863 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2864 if ((*i) == region) {
2868 if (next != regions.end()) {
2870 if ((*next)->locked()) {
2876 if ((*next)->position() != region->last_frame() + 1) {
2877 /* they didn't used to touch, so after shuffle,
2878 just have them swap positions.
2880 new_pos = (*next)->position();
2882 /* they used to touch, so after shuffle,
2883 make sure they still do. put the earlier
2884 region where the later one will end after
2887 new_pos = region->position() + (*next)->length();
2890 (*next)->set_position (region->position());
2891 region->set_position (new_pos);
2893 /* avoid a full sort */
2895 regions.erase (i); // removes the region from the list */
2897 regions.insert (next, region); // adds it back after next
2906 RegionList::iterator prev = regions.end();
2908 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2909 if ((*i) == region) {
2911 if (prev != regions.end()) {
2913 if ((*prev)->locked()) {
2918 if (region->position() != (*prev)->last_frame() + 1) {
2919 /* they didn't used to touch, so after shuffle,
2920 just have them swap positions.
2922 new_pos = region->position();
2924 /* they used to touch, so after shuffle,
2925 make sure they still do. put the earlier
2926 one where the later one will end after
2928 new_pos = (*prev)->position() + region->length();
2931 region->set_position ((*prev)->position());
2932 (*prev)->set_position (new_pos);
2934 /* avoid a full sort */
2936 regions.erase (i); // remove region
2937 regions.insert (prev, region); // insert region before prev
2953 check_dependents (region, false);
2955 notify_contents_changed();
2961 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2963 RegionLock rlock (const_cast<Playlist*> (this));
2965 if (regions.size() > 1) {
2973 Playlist::update_after_tempo_map_change ()
2975 RegionLock rlock (const_cast<Playlist*> (this));
2976 RegionList copy (regions.rlist());
2980 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2981 (*i)->update_position_after_tempo_map_change ();
2988 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2990 RegionLock rl (this, false);
2991 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2997 Playlist::set_explicit_relayering (bool e)
2999 if (e == false && _explicit_relayering == true) {
3001 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
3002 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
3003 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
3004 at this point would keep regions on the same layers.
3006 From then on in, it's just you and your towel.
3009 RegionLock rl (this);
3010 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
3011 (*i)->set_last_layer_op ((*i)->layer ());
3015 _explicit_relayering = e;
3020 Playlist::has_region_at (framepos_t const p) const
3022 RegionLock (const_cast<Playlist *> (this));
3024 RegionList::const_iterator i = regions.begin ();
3025 while (i != regions.end() && !(*i)->covers (p)) {
3029 return (i != regions.end());
3032 /** Remove any region that uses a given source */
3034 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
3036 RegionLock rl (this);
3038 RegionList::iterator i = regions.begin();
3039 while (i != regions.end()) {
3040 RegionList::iterator j = i;
3043 if ((*i)->uses_source (s)) {
3044 remove_region_internal (*i);
3051 /** Look from a session frame time and find the start time of the next region
3052 * which is on the top layer of this playlist.
3053 * @param t Time to look from.
3054 * @return Position of next top-layered region, or max_framepos if there isn't one.
3057 Playlist::find_next_top_layer_position (framepos_t t) const
3059 RegionLock rlock (const_cast<Playlist *> (this));
3061 layer_t const top = top_layer ();
3063 RegionList copy = regions.rlist ();
3064 copy.sort (RegionSortByPosition ());
3066 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3067 if ((*i)->position() >= t && (*i)->layer() == top) {
3068 return (*i)->position();
3072 return max_framepos;
3075 boost::shared_ptr<Region>
3076 Playlist::combine (const RegionList& r)
3079 uint32_t channels = 0;
3081 framepos_t earliest_position = max_framepos;
3082 vector<TwoRegions> old_and_new_regions;
3083 vector<boost::shared_ptr<Region> > originals;
3084 vector<boost::shared_ptr<Region> > copies;
3087 uint32_t max_level = 0;
3089 /* find the maximum depth of all the regions we're combining */
3091 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3092 max_level = max (max_level, (*i)->max_source_level());
3095 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3096 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3098 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3100 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3101 earliest_position = min (earliest_position, (*i)->position());
3104 /* enable this so that we do not try to create xfades etc. as we add
3108 pl->in_partition = true;
3110 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3112 /* copy the region */
3114 boost::shared_ptr<Region> original_region = (*i);
3115 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3117 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3118 originals.push_back (original_region);
3119 copies.push_back (copied_region);
3121 RegionFactory::add_compound_association (original_region, copied_region);
3123 /* make position relative to zero */
3125 pl->add_region (copied_region, original_region->position() - earliest_position);
3127 /* use the maximum number of channels for any region */
3129 channels = max (channels, original_region->n_channels());
3131 /* it will go above the layer of the highest existing region */
3133 layer = max (layer, original_region->layer());
3136 pl->in_partition = false;
3138 pre_combine (copies);
3140 /* add any dependent regions to the new playlist */
3142 copy_dependents (old_and_new_regions, pl.get());
3144 /* now create a new PlaylistSource for each channel in the new playlist */
3147 pair<framepos_t,framepos_t> extent = pl->get_extent();
3149 for (uint32_t chn = 0; chn < channels; ++chn) {
3150 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3154 /* now a new whole-file region using the list of sources */
3156 plist.add (Properties::start, 0);
3157 plist.add (Properties::length, extent.second);
3158 plist.add (Properties::name, parent_name);
3159 plist.add (Properties::whole_file, true);
3161 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3163 /* now the non-whole-file region that we will actually use in the
3168 plist.add (Properties::start, 0);
3169 plist.add (Properties::length, extent.second);
3170 plist.add (Properties::name, child_name);
3171 plist.add (Properties::layer, layer+1);
3173 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3175 /* remove all the selected regions from the current playlist
3180 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3184 /* do type-specific stuff with the originals and the new compound
3188 post_combine (originals, compound_region);
3190 /* add the new region at the right location */
3192 add_region (compound_region, earliest_position);
3198 return compound_region;
3202 Playlist::uncombine (boost::shared_ptr<Region> target)
3204 boost::shared_ptr<PlaylistSource> pls;
3205 boost::shared_ptr<const Playlist> pl;
3206 vector<boost::shared_ptr<Region> > originals;
3207 vector<TwoRegions> old_and_new_regions;
3209 // (1) check that its really a compound region
3211 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3215 pl = pls->playlist();
3217 framepos_t adjusted_start = 0; // gcc isn't smart enough
3218 framepos_t adjusted_end = 0; // gcc isn't smart enough
3220 /* the leftmost (earliest) edge of the compound region
3221 starts at zero in its source, or larger if it
3222 has been trimmed or content-scrolled.
3224 the rightmost (latest) edge of the compound region
3225 relative to its source is the starting point plus
3226 the length of the region.
3229 // (2) get all the original regions
3231 const RegionList& rl (pl->region_list().rlist());
3232 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3233 frameoffset_t move_offset = 0;
3235 /* there are two possibilities here:
3236 1) the playlist that the playlist source was based on
3237 is us, so just add the originals (which belonged to
3238 us anyway) back in the right place.
3240 2) the playlist that the playlist source was based on
3241 is NOT us, so we need to make copies of each of
3242 the original regions that we find, and add them
3245 bool same_playlist = (pls->original() == id());
3247 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3249 boost::shared_ptr<Region> current (*i);
3251 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3253 if (ca == cassocs.end()) {
3257 boost::shared_ptr<Region> original (ca->second);
3258 bool modified_region;
3260 if (i == rl.begin()) {
3261 move_offset = (target->position() - original->position()) - target->start();
3262 adjusted_start = original->position() + target->start();
3263 adjusted_end = adjusted_start + target->length();
3266 if (!same_playlist) {
3267 framepos_t pos = original->position();
3268 /* make a copy, but don't announce it */
3269 original = RegionFactory::create (original, false);
3270 /* the pure copy constructor resets position() to zero,
3273 original->set_position (pos);
3276 /* check to see how the original region (in the
3277 * playlist before compounding occured) overlaps
3278 * with the new state of the compound region.
3281 original->clear_changes ();
3282 modified_region = false;
3284 switch (original->coverage (adjusted_start, adjusted_end)) {
3286 /* original region does not cover any part
3287 of the current state of the compound region
3291 case OverlapInternal:
3292 /* overlap is just a small piece inside the
3293 * original so trim both ends
3295 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3296 modified_region = true;
3299 case OverlapExternal:
3300 /* overlap fully covers original, so leave it
3306 /* overlap starts within but covers end,
3307 so trim the front of the region
3309 original->trim_front (adjusted_start);
3310 modified_region = true;
3314 /* overlap covers start but ends within, so
3315 * trim the end of the region.
3317 original->trim_end (adjusted_end);
3318 modified_region = true;
3323 /* fix the position to match any movement of the compound region.
3325 original->set_position (original->position() + move_offset);
3326 modified_region = true;
3329 if (modified_region) {
3330 _session.add_command (new StatefulDiffCommand (original));
3333 /* and add to the list of regions waiting to be
3337 originals.push_back (original);
3338 old_and_new_regions.push_back (TwoRegions (*i, original));
3341 pre_uncombine (originals, target);
3343 in_partition = true;
3346 // (3) remove the compound region
3348 remove_region (target);
3350 // (4) add the constituent regions
3352 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3353 add_region ((*i), (*i)->position());
3356 /* now move dependent regions back from the compound to this playlist */
3358 pl->copy_dependents (old_and_new_regions, this);
3360 in_partition = false;
3365 Playlist::max_source_level () const
3367 RegionLock rlock (const_cast<Playlist *> (this));
3370 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3371 lvl = max (lvl, (*i)->max_source_level());
3379 Playlist::count_joined_regions () const
3381 RegionLock rlock (const_cast<Playlist *> (this));
3384 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3385 if ((*i)->max_source_level() > 0) {