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;
269 /* this constructor does NOT notify others (session) */
276 InUse (true); /* EMIT SIGNAL */
287 InUse (false); /* EMIT SIGNAL */
292 Playlist::copy_regions (RegionList& newlist) const
294 RegionLock rlock (const_cast<Playlist *> (this));
296 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
297 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
302 Playlist::init (bool hide)
304 add_property (regions);
305 _xml_node_name = X_("Playlist");
307 g_atomic_int_set (&block_notifications, 0);
308 g_atomic_int_set (&ignore_state_changes, 0);
309 pending_contents_change = false;
310 pending_length = false;
311 pending_layering = false;
312 first_set_state = true;
320 _edit_mode = Config->get_edit_mode();
322 in_partition = false;
324 _read_data_count = 0;
326 layer_op_counter = 0;
328 _explicit_relayering = false;
331 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
332 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
334 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
337 Playlist::~Playlist ()
339 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
342 RegionLock rl (this);
344 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
345 (*i)->set_playlist (boost::shared_ptr<Playlist>());
349 /* GoingAway must be emitted by derived classes */
353 Playlist::_set_sort_id ()
356 Playlists are given names like <track name>.<id>
357 or <track name>.<edit group name>.<id> where id
358 is an integer. We extract the id and sort by that.
361 size_t dot_position = _name.val().find_last_of(".");
363 if (dot_position == string::npos) {
366 string t = _name.val().substr(dot_position + 1);
369 _sort_id = boost::lexical_cast<int>(t);
372 catch (boost::bad_lexical_cast e) {
379 Playlist::set_name (const string& str)
381 /* in a typical situation, a playlist is being used
382 by one diskstream and also is referenced by the
383 Session. if there are more references than that,
384 then don't change the name.
391 bool ret = SessionObject::set_name(str);
398 /***********************************************************************
399 CHANGE NOTIFICATION HANDLING
401 Notifications must be delayed till the region_lock is released. This
402 is necessary because handlers for the signals may need to acquire
403 the lock (e.g. to read from the playlist).
404 ***********************************************************************/
407 Playlist::begin_undo ()
414 Playlist::end_undo ()
423 delay_notifications ();
424 g_atomic_int_inc (&ignore_state_changes);
427 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
429 Playlist::thaw (bool from_undo)
431 g_atomic_int_dec_and_test (&ignore_state_changes);
432 release_notifications (from_undo);
437 Playlist::delay_notifications ()
439 g_atomic_int_inc (&block_notifications);
440 freeze_length = _get_extent().second;
443 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
445 Playlist::release_notifications (bool from_undo)
447 if (g_atomic_int_dec_and_test (&block_notifications)) {
448 flush_notifications (from_undo);
453 Playlist::notify_contents_changed ()
455 if (holding_state ()) {
456 pending_contents_change = true;
458 pending_contents_change = false;
459 ContentsChanged(); /* EMIT SIGNAL */
464 Playlist::notify_layering_changed ()
466 if (holding_state ()) {
467 pending_layering = true;
469 pending_layering = false;
470 LayeringChanged(); /* EMIT SIGNAL */
475 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
477 if (holding_state ()) {
478 pending_removes.insert (r);
479 pending_contents_change = true;
480 pending_length = true;
482 /* this might not be true, but we have to act
483 as though it could be.
485 pending_length = false;
486 LengthChanged (); /* EMIT SIGNAL */
487 pending_contents_change = false;
488 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
489 ContentsChanged (); /* EMIT SIGNAL */
494 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
496 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
498 if (holding_state ()) {
500 pending_range_moves.push_back (move);
504 list< Evoral::RangeMove<framepos_t> > m;
506 RangesMoved (m, false);
512 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
514 if (r->position() >= r->last_position()) {
515 /* trimmed shorter */
519 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
521 if (holding_state ()) {
523 pending_region_extensions.push_back (extra);
527 list<Evoral::Range<framepos_t> > r;
535 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
537 if (r->length() < r->last_length()) {
538 /* trimmed shorter */
541 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
543 if (holding_state ()) {
545 pending_region_extensions.push_back (extra);
549 list<Evoral::Range<framepos_t> > r;
557 Playlist::notify_region_added (boost::shared_ptr<Region> r)
559 /* the length change might not be true, but we have to act
560 as though it could be.
563 if (holding_state()) {
564 pending_adds.insert (r);
565 pending_contents_change = true;
566 pending_length = true;
569 pending_length = false;
570 LengthChanged (); /* EMIT SIGNAL */
571 pending_contents_change = false;
572 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
573 ContentsChanged (); /* EMIT SIGNAL */
578 Playlist::notify_length_changed ()
580 if (holding_state ()) {
581 pending_length = true;
583 pending_length = false;
584 LengthChanged(); /* EMIT SIGNAL */
585 pending_contents_change = false;
586 ContentsChanged (); /* EMIT SIGNAL */
590 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
592 Playlist::flush_notifications (bool from_undo)
594 set<boost::shared_ptr<Region> > dependent_checks_needed;
595 set<boost::shared_ptr<Region> >::iterator s;
596 uint32_t regions_changed = false;
597 bool check_length = false;
598 framecnt_t old_length = 0;
606 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
607 regions_changed = true;
608 if (!pending_length) {
609 old_length = _get_extent ().second;
614 /* we have no idea what order the regions ended up in pending
615 bounds (it could be based on selection order, for example).
616 so, to preserve layering in the "most recently moved is higher"
617 model, sort them by existing layer, then timestamp them.
620 // RegionSortByLayer cmp;
621 // pending_bounds.sort (cmp);
623 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
624 if (_session.config.get_layer_model() == MoveAddHigher) {
625 timestamp_layer_op (*r);
627 dependent_checks_needed.insert (*r);
630 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
631 remove_dependents (*s);
632 // cerr << _name << " sends RegionRemoved\n";
633 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
636 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
637 // cerr << _name << " sends RegionAdded\n";
638 /* don't emit RegionAdded signal until relayering is done,
639 so that the region is fully setup by the time
640 anyone hear's that its been added
642 dependent_checks_needed.insert (*s);
646 if (old_length != _get_extent().second) {
647 pending_length = true;
648 // cerr << _name << " length has changed\n";
652 if (pending_length || (freeze_length != _get_extent().second)) {
653 pending_length = false;
654 // cerr << _name << " sends LengthChanged\n";
655 LengthChanged(); /* EMIT SIGNAL */
658 if (regions_changed || pending_contents_change) {
662 pending_contents_change = false;
663 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
664 ContentsChanged (); /* EMIT SIGNAL */
665 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
668 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
669 (*s)->clear_changes ();
670 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
673 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
674 check_dependents (*s, false);
677 if (!pending_range_moves.empty ()) {
678 RangesMoved (pending_range_moves, from_undo);
681 if (!pending_region_extensions.empty ()) {
682 RegionsExtended (pending_region_extensions);
691 Playlist::clear_pending ()
693 pending_adds.clear ();
694 pending_removes.clear ();
695 pending_bounds.clear ();
696 pending_range_moves.clear ();
697 pending_region_extensions.clear ();
698 pending_contents_change = false;
699 pending_length = false;
702 /*************************************************************
704 *************************************************************/
707 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
709 RegionLock rlock (this);
710 times = fabs (times);
712 int itimes = (int) floor (times);
714 framepos_t pos = position;
716 if (times == 1 && auto_partition){
717 partition(pos - 1, (pos + region->length()), true);
721 add_region_internal (region, pos);
722 pos += region->length();
727 /* note that itimes can be zero if we being asked to just
728 insert a single fraction of the region.
731 for (int i = 0; i < itimes; ++i) {
732 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
733 add_region_internal (copy, pos);
734 pos += region->length();
737 framecnt_t length = 0;
739 if (floor (times) != times) {
740 length = (framecnt_t) floor (region->length() * (times - floor (times)));
742 RegionFactory::region_name (name, region->name(), false);
747 plist.add (Properties::start, region->start());
748 plist.add (Properties::length, length);
749 plist.add (Properties::name, name);
750 plist.add (Properties::layer, region->layer());
752 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
753 add_region_internal (sub, pos);
757 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
761 Playlist::set_region_ownership ()
763 RegionLock rl (this);
764 RegionList::iterator i;
765 boost::weak_ptr<Playlist> pl (shared_from_this());
767 for (i = regions.begin(); i != regions.end(); ++i) {
768 (*i)->set_playlist (pl);
773 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
775 if (region->data_type() != _type){
779 RegionSortByPosition cmp;
781 framecnt_t old_length = 0;
783 if (!holding_state()) {
784 old_length = _get_extent().second;
787 if (!first_set_state) {
788 boost::shared_ptr<Playlist> foo (shared_from_this());
789 region->set_playlist (boost::weak_ptr<Playlist>(foo));
792 region->set_position (position, this);
794 timestamp_layer_op (region);
796 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
797 all_regions.insert (region);
799 possibly_splice_unlocked (position, region->length(), region);
801 if (!holding_state ()) {
802 /* layers get assigned from XML state, and are not reset during undo/redo */
806 /* we need to notify the existence of new region before checking dependents. Ick. */
808 notify_region_added (region);
810 if (!holding_state ()) {
812 check_dependents (region, false);
814 if (old_length != _get_extent().second) {
815 notify_length_changed ();
819 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
825 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
827 RegionLock rlock (this);
829 bool old_sp = _splicing;
832 remove_region_internal (old);
833 add_region_internal (newr, pos);
837 possibly_splice_unlocked (pos, old->length() - newr->length());
841 Playlist::remove_region (boost::shared_ptr<Region> region)
843 RegionLock rlock (this);
844 remove_region_internal (region);
848 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
850 RegionList::iterator i;
851 framecnt_t old_length = 0;
854 if (!holding_state()) {
855 old_length = _get_extent().second;
860 region->set_playlist (boost::weak_ptr<Playlist>());
863 /* XXX should probably freeze here .... */
865 for (i = regions.begin(); i != regions.end(); ++i) {
868 framepos_t pos = (*i)->position();
869 framecnt_t distance = (*i)->length();
873 possibly_splice_unlocked (pos, -distance);
875 if (!holding_state ()) {
877 remove_dependents (region);
879 if (old_length != _get_extent().second) {
880 notify_length_changed ();
884 notify_region_removed (region);
894 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
896 if (Config->get_use_overlap_equivalency()) {
897 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
898 if ((*i)->overlap_equivalent (other)) {
899 results.push_back ((*i));
903 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
904 if ((*i)->equivalent (other)) {
905 results.push_back ((*i));
912 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
914 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
916 if ((*i) && (*i)->region_list_equivalent (other)) {
917 results.push_back (*i);
923 Playlist::partition (framepos_t start, framepos_t end, bool cut)
927 partition_internal (start, end, cut, thawlist);
929 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
930 (*i)->resume_property_changes ();
935 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
937 RegionList new_regions;
940 RegionLock rlock (this);
942 boost::shared_ptr<Region> region;
943 boost::shared_ptr<Region> current;
945 RegionList::iterator tmp;
947 framepos_t pos1, pos2, pos3, pos4;
951 /* need to work from a copy, because otherwise the regions we add during the process
952 get operated on as well.
955 RegionList copy = regions.rlist();
957 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
964 if (current->first_frame() >= start && current->last_frame() < end) {
967 remove_region_internal (current);
973 /* coverage will return OverlapStart if the start coincides
974 with the end point. we do not partition such a region,
975 so catch this special case.
978 if (current->first_frame() >= end) {
982 if ((overlap = current->coverage (start, end)) == OverlapNone) {
986 pos1 = current->position();
989 pos4 = current->last_frame();
991 if (overlap == OverlapInternal) {
992 /* split: we need 3 new regions, the front, middle and end.
993 cut: we need 2 regions, the front and end.
998 ---------------*************************------------
1001 ---------------*****++++++++++++++++====------------
1003 ---------------*****----------------====------------
1008 /* "middle" ++++++ */
1010 RegionFactory::region_name (new_name, current->name(), false);
1014 plist.add (Properties::start, current->start() + (pos2 - pos1));
1015 plist.add (Properties::length, pos3 - pos2);
1016 plist.add (Properties::name, new_name);
1017 plist.add (Properties::layer, regions.size());
1018 plist.add (Properties::automatic, true);
1019 plist.add (Properties::left_of_split, true);
1020 plist.add (Properties::right_of_split, true);
1022 region = RegionFactory::create (current, plist);
1023 add_region_internal (region, start);
1024 new_regions.push_back (region);
1029 RegionFactory::region_name (new_name, current->name(), false);
1033 plist.add (Properties::start, current->start() + (pos3 - pos1));
1034 plist.add (Properties::length, pos4 - pos3);
1035 plist.add (Properties::name, new_name);
1036 plist.add (Properties::layer, regions.size());
1037 plist.add (Properties::automatic, true);
1038 plist.add (Properties::right_of_split, true);
1040 region = RegionFactory::create (current, plist);
1042 add_region_internal (region, end);
1043 new_regions.push_back (region);
1047 current->suspend_property_changes ();
1048 thawlist.push_back (current);
1049 current->cut_end (pos2 - 1, this);
1051 } else if (overlap == OverlapEnd) {
1055 ---------------*************************------------
1058 ---------------**************+++++++++++------------
1060 ---------------**************-----------------------
1067 RegionFactory::region_name (new_name, current->name(), false);
1071 plist.add (Properties::start, current->start() + (pos2 - pos1));
1072 plist.add (Properties::length, pos4 - pos2);
1073 plist.add (Properties::name, new_name);
1074 plist.add (Properties::layer, regions.size());
1075 plist.add (Properties::automatic, true);
1076 plist.add (Properties::left_of_split, true);
1078 region = RegionFactory::create (current, plist);
1080 add_region_internal (region, start);
1081 new_regions.push_back (region);
1086 current->suspend_property_changes ();
1087 thawlist.push_back (current);
1088 current->cut_end (pos2 - 1, this);
1090 } else if (overlap == OverlapStart) {
1092 /* split: we need 2 regions: the front and the end.
1093 cut: just trim current to skip the cut area
1098 ---------------*************************------------
1102 ---------------****+++++++++++++++++++++------------
1104 -------------------*********************------------
1110 RegionFactory::region_name (new_name, current->name(), false);
1114 plist.add (Properties::start, current->start());
1115 plist.add (Properties::length, pos3 - pos1);
1116 plist.add (Properties::name, new_name);
1117 plist.add (Properties::layer, regions.size());
1118 plist.add (Properties::automatic, true);
1119 plist.add (Properties::right_of_split, true);
1121 region = RegionFactory::create (current, plist);
1123 add_region_internal (region, pos1);
1124 new_regions.push_back (region);
1129 current->suspend_property_changes ();
1130 thawlist.push_back (current);
1131 current->trim_front (pos3, this);
1132 } else if (overlap == OverlapExternal) {
1134 /* split: no split required.
1135 cut: remove the region.
1140 ---------------*************************------------
1144 ---------------*************************------------
1146 ----------------------------------------------------
1151 remove_region_internal (current);
1154 new_regions.push_back (current);
1158 in_partition = false;
1161 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1162 check_dependents (*i, false);
1166 boost::shared_ptr<Playlist>
1167 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1169 boost::shared_ptr<Playlist> ret;
1170 boost::shared_ptr<Playlist> pl;
1173 if (ranges.empty()) {
1174 return boost::shared_ptr<Playlist>();
1177 start = ranges.front().start;
1179 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1181 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1183 if (i == ranges.begin()) {
1187 /* paste the next section into the nascent playlist,
1188 offset to reflect the start of the first range we
1192 ret->paste (pl, (*i).start - start, 1.0f);
1199 boost::shared_ptr<Playlist>
1200 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1202 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1203 return cut_copy (pmf, ranges, result_is_hidden);
1206 boost::shared_ptr<Playlist>
1207 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1209 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1210 return cut_copy (pmf, ranges, result_is_hidden);
1213 boost::shared_ptr<Playlist>
1214 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1216 boost::shared_ptr<Playlist> the_copy;
1217 RegionList thawlist;
1220 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1221 string new_name = _name;
1225 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1226 return boost::shared_ptr<Playlist>();
1229 partition_internal (start, start+cnt-1, true, thawlist);
1231 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1232 (*i)->resume_property_changes();
1238 boost::shared_ptr<Playlist>
1239 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1243 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1244 string new_name = _name;
1248 cnt = min (_get_extent().second - start, cnt);
1249 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1253 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1255 times = fabs (times);
1258 RegionLock rl1 (this);
1259 RegionLock rl2 (other.get());
1261 framecnt_t const old_length = _get_extent().second;
1263 int itimes = (int) floor (times);
1264 framepos_t pos = position;
1265 framecnt_t const shift = other->_get_extent().second;
1266 layer_t top_layer = regions.size();
1269 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1270 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1272 /* put these new regions on top of all existing ones, but preserve
1273 the ordering they had in the original playlist.
1276 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1277 add_region_internal (copy_of_region, (*i)->position() + pos);
1283 /* XXX shall we handle fractional cases at some point? */
1285 if (old_length != _get_extent().second) {
1286 notify_length_changed ();
1297 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1299 times = fabs (times);
1301 RegionLock rl (this);
1302 int itimes = (int) floor (times);
1303 framepos_t pos = position + 1;
1306 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1307 add_region_internal (copy, pos);
1308 pos += region->length();
1311 if (floor (times) != times) {
1312 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1314 RegionFactory::region_name (name, region->name(), false);
1319 plist.add (Properties::start, region->start());
1320 plist.add (Properties::length, length);
1321 plist.add (Properties::name, name);
1323 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1324 add_region_internal (sub, pos);
1330 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1332 RegionLock rlock (this);
1333 RegionList copy (regions.rlist());
1336 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1338 if ((*r)->last_frame() < at) {
1343 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1344 /* intersected region */
1345 if (!move_intersected) {
1350 /* do not move regions glued to music time - that
1351 has to be done separately.
1354 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1355 fixup.push_back (*r);
1359 (*r)->set_position ((*r)->position() + distance, this);
1362 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1363 (*r)->recompute_position_from_lock_style ();
1368 Playlist::split (framepos_t at)
1370 RegionLock rlock (this);
1371 RegionList copy (regions.rlist());
1373 /* use a copy since this operation can modify the region list
1376 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1377 _split_region (*r, at);
1382 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1384 RegionLock rl (this);
1385 _split_region (region, playlist_position);
1389 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1391 if (!region->covers (playlist_position)) {
1395 if (region->position() == playlist_position ||
1396 region->last_frame() == playlist_position) {
1400 boost::shared_ptr<Region> left;
1401 boost::shared_ptr<Region> right;
1402 frameoffset_t before;
1403 frameoffset_t after;
1407 /* split doesn't change anything about length, so don't try to splice */
1409 bool old_sp = _splicing;
1412 before = playlist_position - region->position();
1413 after = region->length() - before;
1415 RegionFactory::region_name (before_name, region->name(), false);
1420 plist.add (Properties::position, region->position ());
1421 plist.add (Properties::length, before);
1422 plist.add (Properties::name, before_name);
1423 plist.add (Properties::left_of_split, true);
1425 /* note: we must use the version of ::create with an offset here,
1426 since it supplies that offset to the Region constructor, which
1427 is necessary to get audio region gain envelopes right.
1429 left = RegionFactory::create (region, 0, plist);
1432 RegionFactory::region_name (after_name, region->name(), false);
1437 plist.add (Properties::position, region->position() + before);
1438 plist.add (Properties::length, after);
1439 plist.add (Properties::name, after_name);
1440 plist.add (Properties::right_of_split, true);
1442 /* same note as above */
1443 right = RegionFactory::create (region, before, plist);
1446 add_region_internal (left, region->position());
1447 add_region_internal (right, region->position() + before);
1449 uint64_t orig_layer_op = region->last_layer_op();
1450 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1451 if ((*i)->last_layer_op() > orig_layer_op) {
1452 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1456 left->set_last_layer_op ( orig_layer_op );
1457 right->set_last_layer_op ( orig_layer_op + 1);
1461 finalize_split_region (region, left, right);
1463 remove_region_internal (region);
1469 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1471 if (_splicing || in_set_state) {
1472 /* don't respond to splicing moves or state setting */
1476 if (_edit_mode == Splice) {
1477 splice_locked (at, distance, exclude);
1482 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1484 if (_splicing || in_set_state) {
1485 /* don't respond to splicing moves or state setting */
1489 if (_edit_mode == Splice) {
1490 splice_unlocked (at, distance, exclude);
1495 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1498 RegionLock rl (this);
1499 core_splice (at, distance, exclude);
1504 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1506 core_splice (at, distance, exclude);
1510 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1514 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1516 if (exclude && (*i) == exclude) {
1520 if ((*i)->position() >= at) {
1521 framepos_t new_pos = (*i)->position() + distance;
1524 } else if (new_pos >= max_framepos - (*i)->length()) {
1525 new_pos = max_framepos - (*i)->length();
1528 (*i)->set_position (new_pos, this);
1534 notify_length_changed ();
1538 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1540 if (in_set_state || _splicing || _nudging || _shuffling) {
1544 if (what_changed.contains (Properties::position)) {
1546 /* remove it from the list then add it back in
1547 the right place again.
1550 RegionSortByPosition cmp;
1552 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1554 if (i == regions.end()) {
1555 /* the region bounds are being modified but its not currently
1556 in the region list. we will use its bounds correctly when/if
1563 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1566 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1568 frameoffset_t delta = 0;
1570 if (what_changed.contains (Properties::position)) {
1571 delta = region->position() - region->last_position();
1574 if (what_changed.contains (Properties::length)) {
1575 delta += region->length() - region->last_length();
1579 possibly_splice (region->last_position() + region->last_length(), delta, region);
1582 if (holding_state ()) {
1583 pending_bounds.push_back (region);
1585 if (_session.config.get_layer_model() == MoveAddHigher) {
1586 /* it moved or changed length, so change the timestamp */
1587 timestamp_layer_op (region);
1590 notify_length_changed ();
1592 check_dependents (region, false);
1598 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1600 boost::shared_ptr<Region> region (weak_region.lock());
1606 /* this makes a virtual call to the right kind of playlist ... */
1608 region_changed (what_changed, region);
1612 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1614 PropertyChange our_interests;
1615 PropertyChange bounds;
1616 PropertyChange pos_and_length;
1619 if (in_set_state || in_flush) {
1623 our_interests.add (Properties::muted);
1624 our_interests.add (Properties::layer);
1625 our_interests.add (Properties::opaque);
1627 bounds.add (Properties::start);
1628 bounds.add (Properties::position);
1629 bounds.add (Properties::length);
1631 pos_and_length.add (Properties::position);
1632 pos_and_length.add (Properties::length);
1634 if (what_changed.contains (bounds)) {
1635 region_bounds_changed (what_changed, region);
1636 save = !(_splicing || _nudging);
1639 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1640 check_dependents (region, false);
1643 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1644 notify_region_moved (region);
1645 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1646 notify_region_end_trimmed (region);
1647 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1648 notify_region_start_trimmed (region);
1651 /* don't notify about layer changes, since we are the only object that can initiate
1652 them, and we notify in ::relayer()
1655 if (what_changed.contains (our_interests)) {
1663 Playlist::drop_regions ()
1665 RegionLock rl (this);
1667 all_regions.clear ();
1671 Playlist::sync_all_regions_with_regions ()
1673 RegionLock rl (this);
1675 all_regions.clear ();
1677 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1678 all_regions.insert (*i);
1683 Playlist::clear (bool with_signals)
1686 RegionLock rl (this);
1688 region_state_changed_connections.drop_connections ();
1690 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1691 pending_removes.insert (*i);
1696 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1697 remove_dependents (*s);
1703 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1704 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1707 pending_removes.clear ();
1708 pending_length = false;
1710 pending_contents_change = false;
1716 /***********************************************************************
1718 **********************************************************************/
1720 Playlist::RegionList *
1721 Playlist::regions_at (framepos_t frame)
1724 RegionLock rlock (this);
1725 return find_regions_at (frame);
1729 Playlist::count_regions_at (framepos_t frame) const
1731 RegionLock rlock (const_cast<Playlist*>(this));
1734 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1735 if ((*i)->covers (frame)) {
1743 boost::shared_ptr<Region>
1744 Playlist::top_region_at (framepos_t frame)
1747 RegionLock rlock (this);
1748 RegionList *rlist = find_regions_at (frame);
1749 boost::shared_ptr<Region> region;
1751 if (rlist->size()) {
1752 RegionSortByLayer cmp;
1754 region = rlist->back();
1761 boost::shared_ptr<Region>
1762 Playlist::top_unmuted_region_at (framepos_t frame)
1765 RegionLock rlock (this);
1766 RegionList *rlist = find_regions_at (frame);
1768 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1770 RegionList::iterator tmp = i;
1773 if ((*i)->muted()) {
1780 boost::shared_ptr<Region> region;
1782 if (rlist->size()) {
1783 RegionSortByLayer cmp;
1785 region = rlist->back();
1792 Playlist::RegionList*
1793 Playlist::regions_to_read (framepos_t start, framepos_t end)
1795 /* Caller must hold lock */
1797 RegionList covering;
1798 set<framepos_t> to_check;
1799 set<boost::shared_ptr<Region> > unique;
1801 to_check.insert (start);
1802 to_check.insert (end);
1804 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1806 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1808 /* find all/any regions that span start+end */
1810 switch ((*i)->coverage (start, end)) {
1814 case OverlapInternal:
1815 covering.push_back (*i);
1816 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1820 to_check.insert ((*i)->position());
1821 if ((*i)->position() != 0) {
1822 to_check.insert ((*i)->position()-1);
1824 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1825 covering.push_back (*i);
1829 to_check.insert ((*i)->last_frame());
1830 to_check.insert ((*i)->last_frame()+1);
1831 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1832 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1833 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1834 covering.push_back (*i);
1837 case OverlapExternal:
1838 covering.push_back (*i);
1839 to_check.insert ((*i)->position());
1840 if ((*i)->position() != 0) {
1841 to_check.insert ((*i)->position()-1);
1843 to_check.insert ((*i)->last_frame());
1844 to_check.insert ((*i)->last_frame()+1);
1845 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1846 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1847 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1851 /* don't go too far */
1853 if ((*i)->position() > end) {
1858 RegionList* rlist = new RegionList;
1860 /* find all the regions that cover each position .... */
1862 if (covering.size() == 1) {
1864 rlist->push_back (covering.front());
1865 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1870 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1874 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1876 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1878 if ((*x)->covers (*t)) {
1879 here.push_back (*x);
1880 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1884 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1891 RegionSortByLayer cmp;
1894 /* ... and get the top/transparent regions at "here" */
1896 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1900 if ((*c)->opaque()) {
1902 /* the other regions at this position are hidden by this one */
1903 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1910 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1911 rlist->push_back (*s);
1914 if (rlist->size() > 1) {
1915 /* now sort by time order */
1917 RegionSortByPosition cmp;
1922 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1927 Playlist::RegionList *
1928 Playlist::find_regions_at (framepos_t frame)
1930 /* Caller must hold lock */
1932 RegionList *rlist = new RegionList;
1934 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1935 if ((*i)->covers (frame)) {
1936 rlist->push_back (*i);
1943 Playlist::RegionList *
1944 Playlist::regions_touched (framepos_t start, framepos_t end)
1946 RegionLock rlock (this);
1947 RegionList *rlist = new RegionList;
1949 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1950 if ((*i)->coverage (start, end) != OverlapNone) {
1951 rlist->push_back (*i);
1959 Playlist::find_next_transient (framepos_t from, int dir)
1961 RegionLock rlock (this);
1962 AnalysisFeatureList points;
1963 AnalysisFeatureList these_points;
1965 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1967 if ((*i)->last_frame() < from) {
1971 if ((*i)->first_frame() > from) {
1976 (*i)->get_transients (these_points);
1978 /* add first frame, just, err, because */
1980 these_points.push_back ((*i)->first_frame());
1982 points.insert (points.end(), these_points.begin(), these_points.end());
1983 these_points.clear ();
1986 if (points.empty()) {
1990 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1991 bool reached = false;
1994 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1999 if (reached && (*x) > from) {
2004 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2009 if (reached && (*x) < from) {
2018 boost::shared_ptr<Region>
2019 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2021 RegionLock rlock (this);
2022 boost::shared_ptr<Region> ret;
2023 framepos_t closest = max_framepos;
2025 bool end_iter = false;
2027 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2031 frameoffset_t distance;
2032 boost::shared_ptr<Region> r = (*i);
2037 pos = r->first_frame ();
2040 pos = r->last_frame ();
2043 pos = r->sync_position ();
2048 case 1: /* forwards */
2051 if ((distance = pos - frame) < closest) {
2060 default: /* backwards */
2063 if ((distance = frame - pos) < closest) {
2080 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2082 RegionLock rlock (this);
2084 framepos_t closest = max_framepos;
2085 framepos_t ret = -1;
2089 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2091 boost::shared_ptr<Region> r = (*i);
2092 frameoffset_t distance;
2094 if (r->first_frame() > frame) {
2096 distance = r->first_frame() - frame;
2098 if (distance < closest) {
2099 ret = r->first_frame();
2104 if (r->last_frame () > frame) {
2106 distance = r->last_frame () - frame;
2108 if (distance < closest) {
2109 ret = r->last_frame ();
2117 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2119 boost::shared_ptr<Region> r = (*i);
2120 frameoffset_t distance;
2122 if (r->last_frame() < frame) {
2124 distance = frame - r->last_frame();
2126 if (distance < closest) {
2127 ret = r->last_frame();
2132 if (r->first_frame() < frame) {
2134 distance = frame - r->first_frame();
2136 if (distance < closest) {
2137 ret = r->first_frame();
2148 /***********************************************************************/
2154 Playlist::mark_session_dirty ()
2156 if (!in_set_state && !holding_state ()) {
2157 _session.set_dirty();
2162 Playlist::rdiff (vector<Command*>& cmds) const
2164 RegionLock rlock (const_cast<Playlist *> (this));
2165 Stateful::rdiff (cmds);
2169 Playlist::clear_owned_changes ()
2171 RegionLock rlock (this);
2172 Stateful::clear_owned_changes ();
2176 Playlist::update (const RegionListProperty::ChangeRecord& change)
2178 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2179 name(), change.added.size(), change.removed.size()));
2182 /* add the added regions */
2183 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2184 add_region ((*i), (*i)->position());
2186 /* remove the removed regions */
2187 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2195 Playlist::set_state (const XMLNode& node, int version)
2199 XMLNodeConstIterator niter;
2200 XMLPropertyList plist;
2201 XMLPropertyConstIterator piter;
2203 boost::shared_ptr<Region> region;
2208 if (node.name() != "Playlist") {
2215 plist = node.properties();
2217 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2221 if (prop->name() == X_("name")) {
2222 _name = prop->value();
2224 } else if (prop->name() == X_("id")) {
2225 _id = prop->value();
2226 } else if (prop->name() == X_("orig-diskstream-id")) {
2227 _orig_diskstream_id = prop->value ();
2228 } else if (prop->name() == X_("frozen")) {
2229 _frozen = string_is_affirmative (prop->value());
2230 } else if (prop->name() == X_("combine-ops")) {
2231 _combine_ops = atoi (prop->value());
2237 nlist = node.children();
2239 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2243 if (child->name() == "Region") {
2245 if ((prop = child->property ("id")) == 0) {
2246 error << _("region state node has no ID, ignored") << endmsg;
2250 ID id = prop->value ();
2252 if ((region = region_by_id (id))) {
2254 region->suspend_property_changes ();
2256 if (region->set_state (*child, version)) {
2257 region->resume_property_changes ();
2261 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2262 region->suspend_property_changes ();
2264 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 /* update dependents, which was not done during add_region_internal
2279 due to in_set_state being true
2282 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2283 check_dependents (*r, false);
2287 notify_contents_changed ();
2290 first_set_state = false;
2295 Playlist::get_state()
2297 return state (true);
2301 Playlist::get_template()
2303 return state (false);
2306 /** @param full_state true to include regions in the returned state, otherwise false.
2309 Playlist::state (bool full_state)
2311 XMLNode *node = new XMLNode (X_("Playlist"));
2314 node->add_property (X_("id"), id().to_s());
2315 node->add_property (X_("name"), _name);
2316 node->add_property (X_("type"), _type.to_string());
2318 _orig_diskstream_id.print (buf, sizeof (buf));
2319 node->add_property (X_("orig-diskstream-id"), buf);
2320 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2323 RegionLock rlock (this, false);
2325 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2326 node->add_property ("combine-ops", buf);
2328 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2329 node->add_child_nocopy ((*i)->get_state());
2334 node->add_child_copy (*_extra_xml);
2341 Playlist::empty() const
2343 RegionLock rlock (const_cast<Playlist *>(this), false);
2344 return regions.empty();
2348 Playlist::n_regions() const
2350 RegionLock rlock (const_cast<Playlist *>(this), false);
2351 return regions.size();
2354 pair<framepos_t, framepos_t>
2355 Playlist::get_extent () const
2357 RegionLock rlock (const_cast<Playlist *>(this), false);
2358 return _get_extent ();
2361 pair<framepos_t, framepos_t>
2362 Playlist::_get_extent () const
2364 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2366 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2367 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2368 if (e.first < ext.first) {
2369 ext.first = e.first;
2371 if (e.second > ext.second) {
2372 ext.second = e.second;
2380 Playlist::bump_name (string name, Session &session)
2382 string newname = name;
2385 newname = bump_name_once (newname, '.');
2386 } while (session.playlists->by_name (newname)!=NULL);
2393 Playlist::top_layer() const
2395 RegionLock rlock (const_cast<Playlist *> (this));
2398 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2399 top = max (top, (*i)->layer());
2405 Playlist::set_edit_mode (EditMode mode)
2410 /********************
2412 ********************/
2415 Playlist::relayer ()
2417 /* never compute layers when changing state for undo/redo or setting from XML */
2419 if (in_update || in_set_state) {
2423 bool changed = false;
2425 /* Build up a new list of regions on each layer, stored in a set of lists
2426 each of which represent some period of time on some layer. The idea
2427 is to avoid having to search the entire region list to establish whether
2428 each region overlaps another */
2430 /* how many pieces to divide this playlist's time up into */
2431 int const divisions = 512;
2433 /* find the start and end positions of the regions on this playlist */
2434 framepos_t start = INT64_MAX;
2436 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2437 start = min (start, (*i)->position());
2438 end = max (end, (*i)->position() + (*i)->length());
2441 /* hence the size of each time division */
2442 double const division_size = (end - start) / double (divisions);
2444 vector<vector<RegionList> > layers;
2445 layers.push_back (vector<RegionList> (divisions));
2447 /* we want to go through regions from desired lowest to desired highest layer,
2448 which depends on the layer model
2451 RegionList copy = regions.rlist();
2453 /* sort according to the model and the layering mode that we're in */
2455 if (_explicit_relayering) {
2457 copy.sort (RegionSortByLayerWithPending ());
2459 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2461 copy.sort (RegionSortByLastLayerOp ());
2466 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2468 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2469 (*i)->set_pending_explicit_relayer (false);
2471 /* find the time divisions that this region covers; if there are no regions on the list,
2472 division_size will equal 0 and in this case we'll just say that
2473 start_division = end_division = 0.
2475 int start_division = 0;
2476 int end_division = 0;
2478 if (division_size > 0) {
2479 start_division = floor ( ((*i)->position() - start) / division_size);
2480 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2481 if (end_division == divisions) {
2486 assert (divisions == 0 || end_division < divisions);
2488 /* find the lowest layer that this region can go on */
2489 size_t j = layers.size();
2491 /* try layer j - 1; it can go on if it overlaps no other region
2492 that is already on that layer
2495 bool overlap = false;
2496 for (int k = start_division; k <= end_division; ++k) {
2497 RegionList::iterator l = layers[j-1][k].begin ();
2498 while (l != layers[j-1][k].end()) {
2499 if ((*l)->overlap_equivalent (*i)) {
2512 /* overlap, so we must use layer j */
2519 if (j == layers.size()) {
2520 /* we need a new layer for this region */
2521 layers.push_back (vector<RegionList> (divisions));
2524 /* put a reference to this region in each of the divisions that it exists in */
2525 for (int k = start_division; k <= end_division; ++k) {
2526 layers[j][k].push_back (*i);
2529 if ((*i)->layer() != j) {
2533 (*i)->set_layer (j);
2537 notify_layering_changed ();
2541 /* XXX these layer functions are all deprecated */
2544 Playlist::raise_region (boost::shared_ptr<Region> region)
2546 uint32_t top = regions.size() - 1;
2547 layer_t target = region->layer() + 1U;
2549 if (target >= top) {
2550 /* its already at the effective top */
2554 move_region_to_layer (target, region, 1);
2558 Playlist::lower_region (boost::shared_ptr<Region> region)
2560 if (region->layer() == 0) {
2561 /* its already at the bottom */
2565 layer_t target = region->layer() - 1U;
2567 move_region_to_layer (target, region, -1);
2571 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2573 /* does nothing useful if layering mode is later=higher */
2574 switch (_session.config.get_layer_model()) {
2581 layer_t top = regions.size() - 1;
2583 if (region->layer() >= top) {
2584 /* already on the top */
2588 move_region_to_layer (top, region, 1);
2589 /* mark the region's last_layer_op as now, so that it remains on top when
2590 doing future relayers (until something else takes over)
2592 timestamp_layer_op (region);
2596 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2598 /* does nothing useful if layering mode is later=higher */
2599 switch (_session.config.get_layer_model()) {
2606 if (region->layer() == 0) {
2607 /* already on the bottom */
2611 move_region_to_layer (0, region, -1);
2612 /* force region's last layer op to zero so that it stays at the bottom
2613 when doing future relayers
2615 region->set_last_layer_op (0);
2619 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2621 RegionList::iterator i;
2622 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2623 list<LayerInfo> layerinfo;
2626 RegionLock rlock (const_cast<Playlist *> (this));
2628 for (i = regions.begin(); i != regions.end(); ++i) {
2638 /* region is moving up, move all regions on intermediate layers
2642 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2643 dest = (*i)->layer() - 1;
2650 /* region is moving down, move all regions on intermediate layers
2654 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2655 dest = (*i)->layer() + 1;
2665 newpair.second = dest;
2667 layerinfo.push_back (newpair);
2673 /* now reset the layers without holding the region lock */
2675 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2676 x->first->set_layer (x->second);
2679 region->set_layer (target_layer);
2681 /* now check all dependents, since we changed the layering */
2683 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2684 check_dependents (x->first, false);
2687 check_dependents (region, false);
2688 notify_layering_changed ();
2696 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2698 RegionList::iterator i;
2704 RegionLock rlock (const_cast<Playlist *> (this));
2706 for (i = regions.begin(); i != regions.end(); ++i) {
2708 if ((*i)->position() >= start) {
2714 if ((*i)->last_frame() > max_framepos - distance) {
2715 new_pos = max_framepos - (*i)->length();
2717 new_pos = (*i)->position() + distance;
2722 if ((*i)->position() > distance) {
2723 new_pos = (*i)->position() - distance;
2729 (*i)->set_position (new_pos, this);
2737 notify_length_changed ();
2743 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2745 RegionLock rlock (const_cast<Playlist*> (this));
2747 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2748 if ((*r)->uses_source (src)) {
2756 boost::shared_ptr<Region>
2757 Playlist::find_region (const ID& id) const
2759 RegionLock rlock (const_cast<Playlist*> (this));
2761 /* searches all regions currently in use by the playlist */
2763 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2764 if ((*i)->id() == id) {
2769 return boost::shared_ptr<Region> ();
2773 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2775 RegionLock rlock (const_cast<Playlist*> (this));
2778 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2787 boost::shared_ptr<Region>
2788 Playlist::region_by_id (const ID& id) const
2790 /* searches all regions ever added to this playlist */
2792 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2793 if ((*i)->id() == id) {
2797 return boost::shared_ptr<Region> ();
2801 Playlist::dump () const
2803 boost::shared_ptr<Region> r;
2805 cerr << "Playlist \"" << _name << "\" " << endl
2806 << regions.size() << " regions "
2809 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2811 cerr << " " << r->name() << " ["
2812 << r->start() << "+" << r->length()
2822 Playlist::set_frozen (bool yn)
2828 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2830 region->set_last_layer_op (++layer_op_counter);
2835 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2839 if (region->locked()) {
2846 RegionLock rlock (const_cast<Playlist*> (this));
2851 RegionList::iterator next;
2853 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2854 if ((*i) == region) {
2858 if (next != regions.end()) {
2860 if ((*next)->locked()) {
2866 if ((*next)->position() != region->last_frame() + 1) {
2867 /* they didn't used to touch, so after shuffle,
2868 just have them swap positions.
2870 new_pos = (*next)->position();
2872 /* they used to touch, so after shuffle,
2873 make sure they still do. put the earlier
2874 region where the later one will end after
2877 new_pos = region->position() + (*next)->length();
2880 (*next)->set_position (region->position(), this);
2881 region->set_position (new_pos, this);
2883 /* avoid a full sort */
2885 regions.erase (i); // removes the region from the list */
2887 regions.insert (next, region); // adds it back after next
2896 RegionList::iterator prev = regions.end();
2898 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2899 if ((*i) == region) {
2901 if (prev != regions.end()) {
2903 if ((*prev)->locked()) {
2908 if (region->position() != (*prev)->last_frame() + 1) {
2909 /* they didn't used to touch, so after shuffle,
2910 just have them swap positions.
2912 new_pos = region->position();
2914 /* they used to touch, so after shuffle,
2915 make sure they still do. put the earlier
2916 one where the later one will end after
2918 new_pos = (*prev)->position() + region->length();
2921 region->set_position ((*prev)->position(), this);
2922 (*prev)->set_position (new_pos, this);
2924 /* avoid a full sort */
2926 regions.erase (i); // remove region
2927 regions.insert (prev, region); // insert region before prev
2943 check_dependents (region, false);
2945 notify_contents_changed();
2951 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2953 RegionLock rlock (const_cast<Playlist*> (this));
2955 if (regions.size() > 1) {
2963 Playlist::update_after_tempo_map_change ()
2965 RegionLock rlock (const_cast<Playlist*> (this));
2966 RegionList copy (regions.rlist());
2970 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2971 (*i)->update_position_after_tempo_map_change ();
2978 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2980 RegionLock rl (this, false);
2981 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2987 Playlist::set_explicit_relayering (bool e)
2989 if (e == false && _explicit_relayering == true) {
2991 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2992 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2993 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2994 at this point would keep regions on the same layers.
2996 From then on in, it's just you and your towel.
2999 RegionLock rl (this);
3000 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
3001 (*i)->set_last_layer_op ((*i)->layer ());
3005 _explicit_relayering = e;
3010 Playlist::has_region_at (framepos_t const p) const
3012 RegionLock (const_cast<Playlist *> (this));
3014 RegionList::const_iterator i = regions.begin ();
3015 while (i != regions.end() && !(*i)->covers (p)) {
3019 return (i != regions.end());
3022 /** Remove any region that uses a given source */
3024 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
3026 RegionLock rl (this);
3028 RegionList::iterator i = regions.begin();
3029 while (i != regions.end()) {
3030 RegionList::iterator j = i;
3033 if ((*i)->uses_source (s)) {
3034 remove_region_internal (*i);
3041 /** Look from a session frame time and find the start time of the next region
3042 * which is on the top layer of this playlist.
3043 * @param t Time to look from.
3044 * @return Position of next top-layered region, or max_framepos if there isn't one.
3047 Playlist::find_next_top_layer_position (framepos_t t) const
3049 RegionLock rlock (const_cast<Playlist *> (this));
3051 layer_t const top = top_layer ();
3053 RegionList copy = regions.rlist ();
3054 copy.sort (RegionSortByPosition ());
3056 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3057 if ((*i)->position() >= t && (*i)->layer() == top) {
3058 return (*i)->position();
3062 return max_framepos;
3065 boost::shared_ptr<Region>
3066 Playlist::combine (const RegionList& r)
3069 uint32_t channels = 0;
3071 framepos_t earliest_position = max_framepos;
3072 vector<TwoRegions> old_and_new_regions;
3073 vector<boost::shared_ptr<Region> > originals;
3074 vector<boost::shared_ptr<Region> > copies;
3077 uint32_t max_level = 0;
3079 /* find the maximum depth of all the regions we're combining */
3081 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3082 max_level = max (max_level, (*i)->max_source_level());
3085 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3086 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3088 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3090 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3091 earliest_position = min (earliest_position, (*i)->position());
3094 /* enable this so that we do not try to create xfades etc. as we add
3098 pl->in_partition = true;
3100 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3102 /* copy the region */
3104 boost::shared_ptr<Region> original_region = (*i);
3105 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3107 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3108 originals.push_back (original_region);
3109 copies.push_back (copied_region);
3111 RegionFactory::add_compound_association (original_region, copied_region);
3113 /* make position relative to zero */
3115 pl->add_region (copied_region, original_region->position() - earliest_position);
3117 /* use the maximum number of channels for any region */
3119 channels = max (channels, original_region->n_channels());
3121 /* it will go above the layer of the highest existing region */
3123 layer = max (layer, original_region->layer());
3126 pl->in_partition = false;
3128 pre_combine (copies);
3130 /* now create a new PlaylistSource for each channel in the new playlist */
3133 pair<framepos_t,framepos_t> extent = pl->get_extent();
3135 for (uint32_t chn = 0; chn < channels; ++chn) {
3136 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3139 /* now a new whole-file region using the list of sources */
3141 plist.add (Properties::start, 0);
3142 plist.add (Properties::length, extent.second);
3143 plist.add (Properties::name, parent_name);
3144 plist.add (Properties::whole_file, true);
3146 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3148 /* now the non-whole-file region that we will actually use in the
3153 plist.add (Properties::start, 0);
3154 plist.add (Properties::length, extent.second);
3155 plist.add (Properties::name, child_name);
3156 plist.add (Properties::layer, layer+1);
3158 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3160 /* add any dependent regions to the new playlist */
3162 copy_dependents (old_and_new_regions, pl);
3164 /* remove all the selected regions from the current playlist
3169 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3173 /* do type-specific stuff with the originals and the new compound
3177 post_combine (originals, compound_region);
3179 /* add the new region at the right location */
3181 add_region (compound_region, earliest_position);
3187 return compound_region;
3191 Playlist::uncombine (boost::shared_ptr<Region> target)
3193 boost::shared_ptr<PlaylistSource> pls;
3194 boost::shared_ptr<const Playlist> pl;
3195 vector<boost::shared_ptr<Region> > originals;
3197 // (1) check that its really a compound region
3199 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3203 pl = pls->playlist();
3205 framepos_t adjusted_start = 0; // gcc isn't smart enough
3206 framepos_t adjusted_end = 0; // gcc isn't smart enough
3208 /* the leftmost (earliest) edge of the compound region
3209 starts at zero in its source, or larger if it
3210 has been trimmed or content-scrolled.
3212 the rightmost (latest) edge of the compound region
3213 relative to its source is the starting point plus
3214 the length of the region.
3217 // (2) get all the original regions
3219 const RegionList& rl (pl->region_list().rlist());
3220 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3221 frameoffset_t move_offset = 0;
3223 /* there are two possibilities here:
3224 1) the playlist that the playlist source was based on
3225 is us, so just add the originals (which belonged to
3226 us anyway) back in the right place.
3228 2) the playlist that the playlist source was based on
3229 is NOT us, so we need to make copies of each of
3230 the original regions that we find, and add them
3233 bool same_playlist = (pls->original() == id());
3235 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3237 boost::shared_ptr<Region> current (*i);
3239 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3241 if (ca == cassocs.end()) {
3245 boost::shared_ptr<Region> original (ca->second);
3246 bool modified_region;
3248 if (i == rl.begin()) {
3249 move_offset = (target->position() - original->position()) - target->start();
3250 adjusted_start = original->position() + target->start();
3251 adjusted_end = adjusted_start + target->length();
3254 if (!same_playlist) {
3255 framepos_t pos = original->position();
3256 /* make a copy, but don't announce it */
3257 original = RegionFactory::create (original, false);
3258 /* the pure copy constructor resets position() to zero,
3261 original->set_position (pos, this);
3264 /* check to see how the original region (in the
3265 * playlist before compounding occured) overlaps
3266 * with the new state of the compound region.
3269 original->clear_changes ();
3270 modified_region = false;
3272 switch (original->coverage (adjusted_start, adjusted_end)) {
3274 /* original region does not cover any part
3275 of the current state of the compound region
3279 case OverlapInternal:
3280 /* overlap is just a small piece inside the
3281 * original so trim both ends
3283 original->trim_to (adjusted_start, adjusted_end - adjusted_start, this);
3284 modified_region = true;
3287 case OverlapExternal:
3288 /* overlap fully covers original, so leave it
3294 /* overlap starts within but covers end,
3295 so trim the front of the region
3297 original->trim_front (adjusted_start, this);
3298 modified_region = true;
3302 /* overlap covers start but ends within, so
3303 * trim the end of the region.
3305 original->trim_end (adjusted_end, this);
3306 modified_region = true;
3311 /* fix the position to match any movement of the compound region.
3313 original->set_position (original->position() + move_offset, this);
3314 modified_region = true;
3317 if (modified_region) {
3318 _session.add_command (new StatefulDiffCommand (original));
3321 /* and add to the list of regions waiting to be
3325 originals.push_back (original);
3328 pre_uncombine (originals, target);
3330 in_partition = true;
3333 // (3) remove the compound region
3335 remove_region (target);
3337 // (4) add the constituent regions
3339 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3340 add_region ((*i), (*i)->position());
3343 in_partition = false;
3348 Playlist::max_source_level () const
3350 RegionLock rlock (const_cast<Playlist *> (this));
3353 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3354 lvl = max (lvl, (*i)->max_source_level());
3362 Playlist::count_joined_regions () const
3364 RegionLock rlock (const_cast<Playlist *> (this));
3367 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3368 if ((*i)->max_source_level() > 0) {