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_track_id (other->_orig_track_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 _frozen = other->_frozen;
191 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
192 : SessionObject(other->_session, str)
194 , _type(other->_type)
195 , _orig_track_id (other->_orig_track_id)
197 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
199 framepos_t end = start + cnt - 1;
205 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
207 boost::shared_ptr<Region> region;
208 boost::shared_ptr<Region> new_region;
209 frameoffset_t offset = 0;
210 framepos_t position = 0;
217 overlap = region->coverage (start, end);
223 case OverlapInternal:
224 offset = start - region->position();
231 position = region->position() - start;
232 len = end - region->position();
236 offset = start - region->position();
238 len = region->length() - offset;
241 case OverlapExternal:
243 position = region->position() - start;
244 len = region->length();
248 RegionFactory::region_name (new_name, region->name(), false);
252 plist.add (Properties::start, region->start() + offset);
253 plist.add (Properties::length, len);
254 plist.add (Properties::name, new_name);
255 plist.add (Properties::layer, region->layer());
256 plist.add (Properties::layering_index, region->layering_index());
258 new_region = RegionFactory::RegionFactory::create (region, plist);
260 add_region_internal (new_region, position);
264 first_set_state = false;
271 InUse (true); /* EMIT SIGNAL */
282 InUse (false); /* EMIT SIGNAL */
287 Playlist::copy_regions (RegionList& newlist) const
289 RegionLock rlock (const_cast<Playlist *> (this));
291 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
292 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
297 Playlist::init (bool hide)
299 add_property (regions);
300 _xml_node_name = X_("Playlist");
302 g_atomic_int_set (&block_notifications, 0);
303 g_atomic_int_set (&ignore_state_changes, 0);
304 pending_contents_change = false;
305 pending_layering = false;
306 first_set_state = true;
314 _edit_mode = Config->get_edit_mode();
316 in_partition = false;
321 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
322 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
324 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
327 Playlist::~Playlist ()
329 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
332 RegionLock rl (this);
334 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
335 (*i)->set_playlist (boost::shared_ptr<Playlist>());
339 /* GoingAway must be emitted by derived classes */
343 Playlist::_set_sort_id ()
346 Playlists are given names like <track name>.<id>
347 or <track name>.<edit group name>.<id> where id
348 is an integer. We extract the id and sort by that.
351 size_t dot_position = _name.val().find_last_of(".");
353 if (dot_position == string::npos) {
356 string t = _name.val().substr(dot_position + 1);
359 _sort_id = boost::lexical_cast<int>(t);
362 catch (boost::bad_lexical_cast e) {
369 Playlist::set_name (const string& str)
371 /* in a typical situation, a playlist is being used
372 by one diskstream and also is referenced by the
373 Session. if there are more references than that,
374 then don't change the name.
381 bool ret = SessionObject::set_name(str);
388 /***********************************************************************
389 CHANGE NOTIFICATION HANDLING
391 Notifications must be delayed till the region_lock is released. This
392 is necessary because handlers for the signals may need to acquire
393 the lock (e.g. to read from the playlist).
394 ***********************************************************************/
397 Playlist::begin_undo ()
404 Playlist::end_undo ()
413 delay_notifications ();
414 g_atomic_int_inc (&ignore_state_changes);
417 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
419 Playlist::thaw (bool from_undo)
421 g_atomic_int_dec_and_test (&ignore_state_changes);
422 release_notifications (from_undo);
427 Playlist::delay_notifications ()
429 g_atomic_int_inc (&block_notifications);
432 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
434 Playlist::release_notifications (bool from_undo)
436 if (g_atomic_int_dec_and_test (&block_notifications)) {
437 flush_notifications (from_undo);
442 Playlist::notify_contents_changed ()
444 if (holding_state ()) {
445 pending_contents_change = true;
447 pending_contents_change = false;
448 ContentsChanged(); /* EMIT SIGNAL */
453 Playlist::notify_layering_changed ()
455 if (holding_state ()) {
456 pending_layering = true;
458 pending_layering = false;
459 LayeringChanged(); /* EMIT SIGNAL */
464 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
466 if (holding_state ()) {
467 pending_removes.insert (r);
468 pending_contents_change = true;
470 /* this might not be true, but we have to act
471 as though it could be.
473 pending_contents_change = false;
474 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
475 ContentsChanged (); /* EMIT SIGNAL */
480 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
482 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
484 if (holding_state ()) {
486 pending_range_moves.push_back (move);
490 list< Evoral::RangeMove<framepos_t> > m;
492 RangesMoved (m, false);
498 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
500 if (r->position() >= r->last_position()) {
501 /* trimmed shorter */
505 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
507 if (holding_state ()) {
509 pending_region_extensions.push_back (extra);
513 list<Evoral::Range<framepos_t> > r;
521 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
523 if (r->length() < r->last_length()) {
524 /* trimmed shorter */
527 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
529 if (holding_state ()) {
531 pending_region_extensions.push_back (extra);
535 list<Evoral::Range<framepos_t> > r;
543 Playlist::notify_region_added (boost::shared_ptr<Region> r)
545 /* the length change might not be true, but we have to act
546 as though it could be.
549 if (holding_state()) {
550 pending_adds.insert (r);
551 pending_contents_change = true;
554 pending_contents_change = false;
555 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
556 ContentsChanged (); /* EMIT SIGNAL */
560 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
562 Playlist::flush_notifications (bool from_undo)
564 set<boost::shared_ptr<Region> > dependent_checks_needed;
565 set<boost::shared_ptr<Region> >::iterator s;
566 bool regions_changed = false;
574 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
575 regions_changed = true;
578 /* we have no idea what order the regions ended up in pending
579 bounds (it could be based on selection order, for example).
580 so, to preserve layering in the "most recently moved is higher"
581 model, sort them by existing layer, then timestamp them.
584 // RegionSortByLayer cmp;
585 // pending_bounds.sort (cmp);
587 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
588 dependent_checks_needed.insert (*r);
591 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
592 remove_dependents (*s);
593 // cerr << _name << " sends RegionRemoved\n";
594 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
597 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
598 // cerr << _name << " sends RegionAdded\n";
599 /* don't emit RegionAdded signal until relayering is done,
600 so that the region is fully setup by the time
601 anyone hear's that its been added
603 dependent_checks_needed.insert (*s);
607 ((regions_changed || pending_contents_change) && !in_set_state) ||
614 if (regions_changed || pending_contents_change) {
615 pending_contents_change = false;
616 ContentsChanged (); /* EMIT SIGNAL */
619 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
620 (*s)->clear_changes ();
621 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
624 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
625 check_dependents (*s, false);
628 if (!pending_range_moves.empty ()) {
629 RangesMoved (pending_range_moves, from_undo);
632 if (!pending_region_extensions.empty ()) {
633 RegionsExtended (pending_region_extensions);
642 Playlist::clear_pending ()
644 pending_adds.clear ();
645 pending_removes.clear ();
646 pending_bounds.clear ();
647 pending_range_moves.clear ();
648 pending_region_extensions.clear ();
649 pending_contents_change = false;
652 /*************************************************************
654 *************************************************************/
656 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
658 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
660 RegionLock rlock (this);
661 times = fabs (times);
663 int itimes = (int) floor (times);
665 framepos_t pos = position;
667 if (times == 1 && auto_partition){
668 partition(pos - 1, (pos + region->length()), true);
672 add_region_internal (region, pos);
673 set_layer (region, DBL_MAX);
674 pos += region->length();
679 /* note that itimes can be zero if we being asked to just
680 insert a single fraction of the region.
683 for (int i = 0; i < itimes; ++i) {
684 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
685 add_region_internal (copy, pos);
686 set_layer (copy, DBL_MAX);
687 pos += region->length();
690 framecnt_t length = 0;
692 if (floor (times) != times) {
693 length = (framecnt_t) floor (region->length() * (times - floor (times)));
695 RegionFactory::region_name (name, region->name(), false);
700 plist.add (Properties::start, region->start());
701 plist.add (Properties::length, length);
702 plist.add (Properties::name, name);
703 plist.add (Properties::layer, region->layer());
705 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
706 add_region_internal (sub, pos);
707 set_layer (sub, DBL_MAX);
711 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
715 Playlist::set_region_ownership ()
717 RegionLock rl (this);
718 RegionList::iterator i;
719 boost::weak_ptr<Playlist> pl (shared_from_this());
721 for (i = regions.begin(); i != regions.end(); ++i) {
722 (*i)->set_playlist (pl);
727 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
729 if (region->data_type() != _type) {
733 RegionSortByPosition cmp;
735 if (!first_set_state) {
736 boost::shared_ptr<Playlist> foo (shared_from_this());
737 region->set_playlist (boost::weak_ptr<Playlist>(foo));
740 region->set_position (position);
742 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
743 all_regions.insert (region);
745 possibly_splice_unlocked (position, region->length(), region);
747 if (!holding_state ()) {
748 /* layers get assigned from XML state, and are not reset during undo/redo */
752 /* we need to notify the existence of new region before checking dependents. Ick. */
754 notify_region_added (region);
756 if (!holding_state ()) {
757 check_dependents (region, false);
760 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
766 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
768 RegionLock rlock (this);
770 bool old_sp = _splicing;
773 remove_region_internal (old);
774 add_region_internal (newr, pos);
775 set_layer (newr, old->layer ());
779 possibly_splice_unlocked (pos, old->length() - newr->length());
783 Playlist::remove_region (boost::shared_ptr<Region> region)
785 RegionLock rlock (this);
786 remove_region_internal (region);
790 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
792 RegionList::iterator i;
796 region->set_playlist (boost::weak_ptr<Playlist>());
799 /* XXX should probably freeze here .... */
801 for (i = regions.begin(); i != regions.end(); ++i) {
804 framepos_t pos = (*i)->position();
805 framecnt_t distance = (*i)->length();
809 possibly_splice_unlocked (pos, -distance);
811 if (!holding_state ()) {
813 remove_dependents (region);
816 notify_region_removed (region);
825 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
827 if (Config->get_use_overlap_equivalency()) {
828 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
829 if ((*i)->overlap_equivalent (other)) {
830 results.push_back ((*i));
834 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
835 if ((*i)->equivalent (other)) {
836 results.push_back ((*i));
843 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
845 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
847 if ((*i) && (*i)->region_list_equivalent (other)) {
848 results.push_back (*i);
854 Playlist::partition (framepos_t start, framepos_t end, bool cut)
858 partition_internal (start, end, cut, thawlist);
860 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
861 (*i)->resume_property_changes ();
865 /** Go through each region on the playlist and cut them at start and end, removing the section between
866 * start and end if cutting == true. Regions that lie entirely within start and end are always
871 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
873 RegionList new_regions;
876 RegionLock rlock (this);
878 boost::shared_ptr<Region> region;
879 boost::shared_ptr<Region> current;
881 RegionList::iterator tmp;
883 framepos_t pos1, pos2, pos3, pos4;
887 /* need to work from a copy, because otherwise the regions we add during the process
888 get operated on as well.
891 RegionList copy = regions.rlist();
893 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
900 if (current->first_frame() >= start && current->last_frame() < end) {
903 remove_region_internal (current);
909 /* coverage will return OverlapStart if the start coincides
910 with the end point. we do not partition such a region,
911 so catch this special case.
914 if (current->first_frame() >= end) {
918 if ((overlap = current->coverage (start, end)) == OverlapNone) {
922 pos1 = current->position();
925 pos4 = current->last_frame();
927 if (overlap == OverlapInternal) {
928 /* split: we need 3 new regions, the front, middle and end.
929 cut: we need 2 regions, the front and end.
934 ---------------*************************------------
937 ---------------*****++++++++++++++++====------------
939 ---------------*****----------------====------------
944 /* "middle" ++++++ */
946 RegionFactory::region_name (new_name, current->name(), false);
950 plist.add (Properties::start, current->start() + (pos2 - pos1));
951 plist.add (Properties::length, pos3 - pos2);
952 plist.add (Properties::name, new_name);
953 plist.add (Properties::layer, current->layer ());
954 plist.add (Properties::layering_index, current->layering_index ());
955 plist.add (Properties::automatic, true);
956 plist.add (Properties::left_of_split, true);
957 plist.add (Properties::right_of_split, true);
959 region = RegionFactory::create (current, plist);
960 add_region_internal (region, start);
961 new_regions.push_back (region);
966 RegionFactory::region_name (new_name, current->name(), false);
970 plist.add (Properties::start, current->start() + (pos3 - pos1));
971 plist.add (Properties::length, pos4 - pos3);
972 plist.add (Properties::name, new_name);
973 plist.add (Properties::layer, current->layer ());
974 plist.add (Properties::layering_index, current->layering_index ());
975 plist.add (Properties::automatic, true);
976 plist.add (Properties::right_of_split, true);
978 region = RegionFactory::create (current, plist);
980 add_region_internal (region, end);
981 new_regions.push_back (region);
985 current->suspend_property_changes ();
986 thawlist.push_back (current);
987 current->cut_end (pos2 - 1);
989 } else if (overlap == OverlapEnd) {
993 ---------------*************************------------
996 ---------------**************+++++++++++------------
998 ---------------**************-----------------------
1005 RegionFactory::region_name (new_name, current->name(), false);
1009 plist.add (Properties::start, current->start() + (pos2 - pos1));
1010 plist.add (Properties::length, pos4 - pos2);
1011 plist.add (Properties::name, new_name);
1012 plist.add (Properties::layer, current->layer ());
1013 plist.add (Properties::layering_index, current->layering_index ());
1014 plist.add (Properties::automatic, true);
1015 plist.add (Properties::left_of_split, true);
1017 region = RegionFactory::create (current, plist);
1019 add_region_internal (region, start);
1020 new_regions.push_back (region);
1025 current->suspend_property_changes ();
1026 thawlist.push_back (current);
1027 current->cut_end (pos2 - 1);
1029 } else if (overlap == OverlapStart) {
1031 /* split: we need 2 regions: the front and the end.
1032 cut: just trim current to skip the cut area
1037 ---------------*************************------------
1041 ---------------****+++++++++++++++++++++------------
1043 -------------------*********************------------
1049 RegionFactory::region_name (new_name, current->name(), false);
1053 plist.add (Properties::start, current->start());
1054 plist.add (Properties::length, pos3 - pos1);
1055 plist.add (Properties::name, new_name);
1056 plist.add (Properties::layer, current->layer ());
1057 plist.add (Properties::layering_index, current->layering_index ());
1058 plist.add (Properties::automatic, true);
1059 plist.add (Properties::right_of_split, true);
1061 region = RegionFactory::create (current, plist);
1063 add_region_internal (region, pos1);
1064 new_regions.push_back (region);
1069 current->suspend_property_changes ();
1070 thawlist.push_back (current);
1071 current->trim_front (pos3);
1072 } else if (overlap == OverlapExternal) {
1074 /* split: no split required.
1075 cut: remove the region.
1080 ---------------*************************------------
1084 ---------------*************************------------
1086 ----------------------------------------------------
1091 remove_region_internal (current);
1094 new_regions.push_back (current);
1098 in_partition = false;
1101 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1102 check_dependents (*i, false);
1106 boost::shared_ptr<Playlist>
1107 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1109 boost::shared_ptr<Playlist> ret;
1110 boost::shared_ptr<Playlist> pl;
1113 if (ranges.empty()) {
1114 return boost::shared_ptr<Playlist>();
1117 start = ranges.front().start;
1119 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1121 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1123 if (i == ranges.begin()) {
1127 /* paste the next section into the nascent playlist,
1128 offset to reflect the start of the first range we
1132 ret->paste (pl, (*i).start - start, 1.0f);
1139 boost::shared_ptr<Playlist>
1140 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1142 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1143 return cut_copy (pmf, ranges, result_is_hidden);
1146 boost::shared_ptr<Playlist>
1147 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1149 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1150 return cut_copy (pmf, ranges, result_is_hidden);
1153 boost::shared_ptr<Playlist>
1154 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1156 boost::shared_ptr<Playlist> the_copy;
1157 RegionList thawlist;
1160 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1161 string new_name = _name;
1165 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1166 return boost::shared_ptr<Playlist>();
1169 partition_internal (start, start+cnt-1, true, thawlist);
1171 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1172 (*i)->resume_property_changes();
1178 boost::shared_ptr<Playlist>
1179 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1183 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1184 string new_name = _name;
1188 cnt = min (_get_extent().second - start, cnt);
1189 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1193 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1195 times = fabs (times);
1198 RegionLock rl1 (this);
1199 RegionLock rl2 (other.get());
1201 int itimes = (int) floor (times);
1202 framepos_t pos = position;
1203 framecnt_t const shift = other->_get_extent().second;
1204 layer_t top = top_layer ();
1207 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1208 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1210 /* put these new regions on top of all existing ones, but preserve
1211 the ordering they had in the original playlist.
1214 add_region_internal (copy_of_region, (*i)->position() + pos);
1215 set_layer (copy_of_region, copy_of_region->layer() + top);
1226 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1228 times = fabs (times);
1230 RegionLock rl (this);
1231 int itimes = (int) floor (times);
1232 framepos_t pos = position + 1;
1235 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1236 add_region_internal (copy, pos);
1237 set_layer (copy, DBL_MAX);
1238 pos += region->length();
1241 if (floor (times) != times) {
1242 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1244 RegionFactory::region_name (name, region->name(), false);
1249 plist.add (Properties::start, region->start());
1250 plist.add (Properties::length, length);
1251 plist.add (Properties::name, name);
1253 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1254 add_region_internal (sub, pos);
1255 set_layer (sub, DBL_MAX);
1261 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1263 RegionLock rlock (this);
1264 RegionList copy (regions.rlist());
1267 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1269 if ((*r)->last_frame() < at) {
1274 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1275 /* intersected region */
1276 if (!move_intersected) {
1281 /* do not move regions glued to music time - that
1282 has to be done separately.
1285 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1286 fixup.push_back (*r);
1290 (*r)->set_position ((*r)->position() + distance);
1293 /* XXX: may not be necessary; Region::post_set should do this, I think */
1294 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1295 (*r)->recompute_position_from_lock_style ();
1300 Playlist::split (framepos_t at)
1302 RegionLock rlock (this);
1303 RegionList copy (regions.rlist());
1305 /* use a copy since this operation can modify the region list
1308 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1309 _split_region (*r, at);
1314 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1316 RegionLock rl (this);
1317 _split_region (region, playlist_position);
1321 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1323 if (!region->covers (playlist_position)) {
1327 if (region->position() == playlist_position ||
1328 region->last_frame() == playlist_position) {
1332 boost::shared_ptr<Region> left;
1333 boost::shared_ptr<Region> right;
1334 frameoffset_t before;
1335 frameoffset_t after;
1339 /* split doesn't change anything about length, so don't try to splice */
1341 bool old_sp = _splicing;
1344 before = playlist_position - region->position();
1345 after = region->length() - before;
1347 RegionFactory::region_name (before_name, region->name(), false);
1352 plist.add (Properties::position, region->position ());
1353 plist.add (Properties::length, before);
1354 plist.add (Properties::name, before_name);
1355 plist.add (Properties::left_of_split, true);
1356 plist.add (Properties::layering_index, region->layering_index ());
1357 plist.add (Properties::layer, region->layer ());
1359 /* note: we must use the version of ::create with an offset here,
1360 since it supplies that offset to the Region constructor, which
1361 is necessary to get audio region gain envelopes right.
1363 left = RegionFactory::create (region, 0, plist);
1366 RegionFactory::region_name (after_name, region->name(), false);
1371 plist.add (Properties::position, region->position() + before);
1372 plist.add (Properties::length, after);
1373 plist.add (Properties::name, after_name);
1374 plist.add (Properties::right_of_split, true);
1375 plist.add (Properties::layering_index, region->layering_index ());
1376 plist.add (Properties::layer, region->layer ());
1378 /* same note as above */
1379 right = RegionFactory::create (region, before, plist);
1382 add_region_internal (left, region->position());
1383 add_region_internal (right, region->position() + before);
1385 finalize_split_region (region, left, right);
1387 remove_region_internal (region);
1393 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1395 if (_splicing || in_set_state) {
1396 /* don't respond to splicing moves or state setting */
1400 if (_edit_mode == Splice) {
1401 splice_locked (at, distance, exclude);
1406 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1408 if (_splicing || in_set_state) {
1409 /* don't respond to splicing moves or state setting */
1413 if (_edit_mode == Splice) {
1414 splice_unlocked (at, distance, exclude);
1419 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1422 RegionLock rl (this);
1423 core_splice (at, distance, exclude);
1428 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1430 core_splice (at, distance, exclude);
1434 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1438 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1440 if (exclude && (*i) == exclude) {
1444 if ((*i)->position() >= at) {
1445 framepos_t new_pos = (*i)->position() + distance;
1448 } else if (new_pos >= max_framepos - (*i)->length()) {
1449 new_pos = max_framepos - (*i)->length();
1452 (*i)->set_position (new_pos);
1458 notify_contents_changed ();
1462 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1464 if (in_set_state || _splicing || _nudging || _shuffling) {
1468 if (what_changed.contains (Properties::position)) {
1470 /* remove it from the list then add it back in
1471 the right place again.
1474 RegionSortByPosition cmp;
1476 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1478 if (i == regions.end()) {
1479 /* the region bounds are being modified but its not currently
1480 in the region list. we will use its bounds correctly when/if
1487 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1490 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1492 frameoffset_t delta = 0;
1494 if (what_changed.contains (Properties::position)) {
1495 delta = region->position() - region->last_position();
1498 if (what_changed.contains (Properties::length)) {
1499 delta += region->length() - region->last_length();
1503 possibly_splice (region->last_position() + region->last_length(), delta, region);
1506 if (holding_state ()) {
1507 pending_bounds.push_back (region);
1509 notify_contents_changed ();
1511 check_dependents (region, false);
1517 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1519 boost::shared_ptr<Region> region (weak_region.lock());
1525 /* this makes a virtual call to the right kind of playlist ... */
1527 region_changed (what_changed, region);
1531 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1533 PropertyChange our_interests;
1534 PropertyChange bounds;
1535 PropertyChange pos_and_length;
1538 if (in_set_state || in_flush) {
1542 our_interests.add (Properties::muted);
1543 our_interests.add (Properties::layer);
1544 our_interests.add (Properties::opaque);
1546 bounds.add (Properties::start);
1547 bounds.add (Properties::position);
1548 bounds.add (Properties::length);
1550 pos_and_length.add (Properties::position);
1551 pos_and_length.add (Properties::length);
1553 if (what_changed.contains (bounds)) {
1554 region_bounds_changed (what_changed, region);
1555 save = !(_splicing || _nudging);
1558 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1559 check_dependents (region, false);
1562 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1563 notify_region_moved (region);
1564 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1565 notify_region_end_trimmed (region);
1566 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1567 notify_region_start_trimmed (region);
1570 /* don't notify about layer changes, since we are the only object that can initiate
1571 them, and we notify in ::relayer()
1574 if (what_changed.contains (our_interests)) {
1582 Playlist::drop_regions ()
1584 RegionLock rl (this);
1586 all_regions.clear ();
1590 Playlist::sync_all_regions_with_regions ()
1592 RegionLock rl (this);
1594 all_regions.clear ();
1596 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1597 all_regions.insert (*i);
1602 Playlist::clear (bool with_signals)
1605 RegionLock rl (this);
1607 region_state_changed_connections.drop_connections ();
1609 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1610 pending_removes.insert (*i);
1615 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1616 remove_dependents (*s);
1622 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1623 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1626 pending_removes.clear ();
1627 pending_contents_change = false;
1633 /***********************************************************************
1635 **********************************************************************/
1637 Playlist::RegionList *
1638 Playlist::regions_at (framepos_t frame)
1641 RegionLock rlock (this);
1642 return find_regions_at (frame);
1646 Playlist::count_regions_at (framepos_t frame) const
1648 RegionLock rlock (const_cast<Playlist*>(this));
1651 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1652 if ((*i)->covers (frame)) {
1660 boost::shared_ptr<Region>
1661 Playlist::top_region_at (framepos_t frame)
1664 RegionLock rlock (this);
1665 RegionList *rlist = find_regions_at (frame);
1666 boost::shared_ptr<Region> region;
1668 if (rlist->size()) {
1669 RegionSortByLayer cmp;
1671 region = rlist->back();
1678 boost::shared_ptr<Region>
1679 Playlist::top_unmuted_region_at (framepos_t frame)
1682 RegionLock rlock (this);
1683 RegionList *rlist = find_regions_at (frame);
1685 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1687 RegionList::iterator tmp = i;
1690 if ((*i)->muted()) {
1697 boost::shared_ptr<Region> region;
1699 if (rlist->size()) {
1700 RegionSortByLayer cmp;
1702 region = rlist->back();
1709 Playlist::RegionList*
1710 Playlist::regions_to_read (framepos_t start, framepos_t end)
1712 /* Caller must hold lock */
1714 RegionList covering;
1715 set<framepos_t> to_check;
1716 set<boost::shared_ptr<Region> > unique;
1718 to_check.insert (start);
1719 to_check.insert (end);
1721 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1723 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1725 /* find all/any regions that span start+end */
1727 switch ((*i)->coverage (start, end)) {
1731 case OverlapInternal:
1732 covering.push_back (*i);
1733 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1737 to_check.insert ((*i)->position());
1738 if ((*i)->position() != 0) {
1739 to_check.insert ((*i)->position()-1);
1741 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1742 covering.push_back (*i);
1746 to_check.insert ((*i)->last_frame());
1747 to_check.insert ((*i)->last_frame()+1);
1748 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1749 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1750 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1751 covering.push_back (*i);
1754 case OverlapExternal:
1755 covering.push_back (*i);
1756 to_check.insert ((*i)->position());
1757 if ((*i)->position() != 0) {
1758 to_check.insert ((*i)->position()-1);
1760 to_check.insert ((*i)->last_frame());
1761 to_check.insert ((*i)->last_frame()+1);
1762 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1763 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1764 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1768 /* don't go too far */
1770 if ((*i)->position() > end) {
1775 RegionList* rlist = new RegionList;
1777 /* find all the regions that cover each position .... */
1779 if (covering.size() == 1) {
1781 rlist->push_back (covering.front());
1782 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1787 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1791 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1793 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1795 if ((*x)->covers (*t)) {
1796 here.push_back (*x);
1797 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1801 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1808 RegionSortByLayer cmp;
1811 /* ... and get the top/transparent regions at "here" */
1813 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1817 if ((*c)->opaque()) {
1819 /* the other regions at this position are hidden by this one */
1820 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1827 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1828 rlist->push_back (*s);
1831 if (rlist->size() > 1) {
1832 /* now sort by time order */
1834 RegionSortByPosition cmp;
1839 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1844 Playlist::RegionList *
1845 Playlist::find_regions_at (framepos_t frame)
1847 /* Caller must hold lock */
1849 RegionList *rlist = new RegionList;
1851 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1852 if ((*i)->covers (frame)) {
1853 rlist->push_back (*i);
1860 Playlist::RegionList *
1861 Playlist::regions_touched (framepos_t start, framepos_t end)
1863 RegionLock rlock (this);
1864 RegionList *rlist = new RegionList;
1866 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1867 if ((*i)->coverage (start, end) != OverlapNone) {
1868 rlist->push_back (*i);
1876 Playlist::find_next_transient (framepos_t from, int dir)
1878 RegionLock rlock (this);
1879 AnalysisFeatureList points;
1880 AnalysisFeatureList these_points;
1882 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1884 if ((*i)->last_frame() < from) {
1888 if ((*i)->first_frame() > from) {
1893 (*i)->get_transients (these_points);
1895 /* add first frame, just, err, because */
1897 these_points.push_back ((*i)->first_frame());
1899 points.insert (points.end(), these_points.begin(), these_points.end());
1900 these_points.clear ();
1903 if (points.empty()) {
1907 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1908 bool reached = false;
1911 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1916 if (reached && (*x) > from) {
1921 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1926 if (reached && (*x) < from) {
1935 boost::shared_ptr<Region>
1936 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1938 RegionLock rlock (this);
1939 boost::shared_ptr<Region> ret;
1940 framepos_t closest = max_framepos;
1942 bool end_iter = false;
1944 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1948 frameoffset_t distance;
1949 boost::shared_ptr<Region> r = (*i);
1954 pos = r->first_frame ();
1957 pos = r->last_frame ();
1960 pos = r->sync_position ();
1965 case 1: /* forwards */
1968 if ((distance = pos - frame) < closest) {
1977 default: /* backwards */
1980 if ((distance = frame - pos) < closest) {
1997 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1999 RegionLock rlock (this);
2001 framepos_t closest = max_framepos;
2002 framepos_t ret = -1;
2006 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2008 boost::shared_ptr<Region> r = (*i);
2009 frameoffset_t distance;
2011 if (r->first_frame() > frame) {
2013 distance = r->first_frame() - frame;
2015 if (distance < closest) {
2016 ret = r->first_frame();
2021 if (r->last_frame () > frame) {
2023 distance = r->last_frame () - frame;
2025 if (distance < closest) {
2026 ret = r->last_frame ();
2034 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2036 boost::shared_ptr<Region> r = (*i);
2037 frameoffset_t distance;
2039 if (r->last_frame() < frame) {
2041 distance = frame - r->last_frame();
2043 if (distance < closest) {
2044 ret = r->last_frame();
2049 if (r->first_frame() < frame) {
2051 distance = frame - r->first_frame();
2053 if (distance < closest) {
2054 ret = r->first_frame();
2065 /***********************************************************************/
2071 Playlist::mark_session_dirty ()
2073 if (!in_set_state && !holding_state ()) {
2074 _session.set_dirty();
2079 Playlist::rdiff (vector<Command*>& cmds) const
2081 RegionLock rlock (const_cast<Playlist *> (this));
2082 Stateful::rdiff (cmds);
2086 Playlist::clear_owned_changes ()
2088 RegionLock rlock (this);
2089 Stateful::clear_owned_changes ();
2093 Playlist::update (const RegionListProperty::ChangeRecord& change)
2095 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2096 name(), change.added.size(), change.removed.size()));
2099 /* add the added regions */
2100 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2101 add_region_internal ((*i), (*i)->position());
2103 /* remove the removed regions */
2104 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2112 Playlist::set_state (const XMLNode& node, int version)
2116 XMLNodeConstIterator niter;
2117 XMLPropertyList plist;
2118 XMLPropertyConstIterator piter;
2120 boost::shared_ptr<Region> region;
2122 bool seen_region_nodes = false;
2127 if (node.name() != "Playlist") {
2134 plist = node.properties();
2138 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2142 if (prop->name() == X_("name")) {
2143 _name = prop->value();
2145 } else if (prop->name() == X_("orig-diskstream-id")) {
2146 /* XXX legacy session: fix up later */
2147 _orig_track_id = prop->value ();
2148 } else if (prop->name() == X_("orig-track-id")) {
2149 _orig_track_id = prop->value ();
2150 } else if (prop->name() == X_("frozen")) {
2151 _frozen = string_is_affirmative (prop->value());
2152 } else if (prop->name() == X_("combine-ops")) {
2153 _combine_ops = atoi (prop->value());
2159 nlist = node.children();
2161 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2165 if (child->name() == "Region") {
2167 seen_region_nodes = true;
2169 if ((prop = child->property ("id")) == 0) {
2170 error << _("region state node has no ID, ignored") << endmsg;
2174 ID id = prop->value ();
2176 if ((region = region_by_id (id))) {
2178 region->suspend_property_changes ();
2180 if (region->set_state (*child, version)) {
2181 region->resume_property_changes ();
2185 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2186 region->suspend_property_changes ();
2188 error << _("Playlist: cannot create region from XML") << endmsg;
2193 RegionLock rlock (this);
2194 add_region_internal (region, region->position());
2197 region->resume_property_changes ();
2202 if (seen_region_nodes && regions.empty()) {
2206 /* update dependents, which was not done during add_region_internal
2207 due to in_set_state being true
2210 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2211 check_dependents (*r, false);
2216 notify_contents_changed ();
2219 first_set_state = false;
2225 Playlist::get_state()
2227 return state (true);
2231 Playlist::get_template()
2233 return state (false);
2236 /** @param full_state true to include regions in the returned state, otherwise false.
2239 Playlist::state (bool full_state)
2241 XMLNode *node = new XMLNode (X_("Playlist"));
2244 node->add_property (X_("id"), id().to_s());
2245 node->add_property (X_("name"), _name);
2246 node->add_property (X_("type"), _type.to_string());
2248 _orig_track_id.print (buf, sizeof (buf));
2249 node->add_property (X_("orig-track-id"), buf);
2250 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2253 RegionLock rlock (this, false);
2255 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2256 node->add_property ("combine-ops", buf);
2258 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2259 node->add_child_nocopy ((*i)->get_state());
2264 node->add_child_copy (*_extra_xml);
2271 Playlist::empty() const
2273 RegionLock rlock (const_cast<Playlist *>(this), false);
2274 return regions.empty();
2278 Playlist::n_regions() const
2280 RegionLock rlock (const_cast<Playlist *>(this), false);
2281 return regions.size();
2284 pair<framepos_t, framepos_t>
2285 Playlist::get_extent () const
2287 RegionLock rlock (const_cast<Playlist *>(this), false);
2288 return _get_extent ();
2291 pair<framepos_t, framepos_t>
2292 Playlist::_get_extent () const
2294 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2296 if (regions.empty()) {
2301 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2302 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2303 if (e.first < ext.first) {
2304 ext.first = e.first;
2306 if (e.second > ext.second) {
2307 ext.second = e.second;
2315 Playlist::bump_name (string name, Session &session)
2317 string newname = name;
2320 newname = bump_name_once (newname, '.');
2321 } while (session.playlists->by_name (newname)!=NULL);
2328 Playlist::top_layer() const
2330 RegionLock rlock (const_cast<Playlist *> (this));
2333 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2334 top = max (top, (*i)->layer());
2340 Playlist::set_edit_mode (EditMode mode)
2345 struct RelayerSort {
2346 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2347 return a->layering_index() < b->layering_index();
2351 /** Set a new layer for a region. This adjusts the layering indices of all
2352 * regions in the playlist to put the specified region in the appropriate
2353 * place. The actual layering will be fixed up when relayer() happens.
2357 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2359 /* Remove the layer we are setting from our region list, and sort it */
2360 RegionList copy = regions.rlist();
2361 copy.remove (region);
2362 copy.sort (RelayerSort ());
2364 /* Put region back in the right place */
2365 RegionList::iterator i = copy.begin();
2366 while (i != copy.end ()) {
2367 if ((*i)->layer() > new_layer) {
2373 copy.insert (i, region);
2375 setup_layering_indices (copy);
2379 Playlist::setup_layering_indices (RegionList const & regions) const
2382 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2383 (*k)->set_layering_index (j++);
2388 /** Take the layering indices of each of our regions, compute the layers
2389 * that they should be on, and write the layers back to the regions.
2392 Playlist::relayer ()
2394 /* never compute layers when setting from XML */
2400 bool changed = false;
2402 /* Build up a new list of regions on each layer, stored in a set of lists
2403 each of which represent some period of time on some layer. The idea
2404 is to avoid having to search the entire region list to establish whether
2405 each region overlaps another */
2407 /* how many pieces to divide this playlist's time up into */
2408 int const divisions = 512;
2410 /* find the start and end positions of the regions on this playlist */
2411 framepos_t start = INT64_MAX;
2413 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2414 start = min (start, (*i)->position());
2415 end = max (end, (*i)->position() + (*i)->length());
2418 /* hence the size of each time division */
2419 double const division_size = (end - start) / double (divisions);
2421 vector<vector<RegionList> > layers;
2422 layers.push_back (vector<RegionList> (divisions));
2424 /* Sort our regions into layering index order */
2425 RegionList copy = regions.rlist();
2426 copy.sort (RelayerSort ());
2428 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2429 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2430 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2433 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2435 /* find the time divisions that this region covers; if there are no regions on the list,
2436 division_size will equal 0 and in this case we'll just say that
2437 start_division = end_division = 0.
2439 int start_division = 0;
2440 int end_division = 0;
2442 if (division_size > 0) {
2443 start_division = floor ( ((*i)->position() - start) / division_size);
2444 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2445 if (end_division == divisions) {
2450 assert (divisions == 0 || end_division < divisions);
2452 /* find the lowest layer that this region can go on */
2453 size_t j = layers.size();
2455 /* try layer j - 1; it can go on if it overlaps no other region
2456 that is already on that layer
2459 bool overlap = false;
2460 for (int k = start_division; k <= end_division; ++k) {
2461 RegionList::iterator l = layers[j-1][k].begin ();
2462 while (l != layers[j-1][k].end()) {
2463 if ((*l)->overlap_equivalent (*i)) {
2476 /* overlap, so we must use layer j */
2483 if (j == layers.size()) {
2484 /* we need a new layer for this region */
2485 layers.push_back (vector<RegionList> (divisions));
2488 /* put a reference to this region in each of the divisions that it exists in */
2489 for (int k = start_division; k <= end_division; ++k) {
2490 layers[j][k].push_back (*i);
2493 if ((*i)->layer() != j) {
2497 (*i)->set_layer (j);
2501 notify_layering_changed ();
2504 /* This relayer() may have been called as a result of a region removal, in which
2505 case we need to setup layering indices so account for the one that has just
2508 setup_layering_indices (copy);
2512 Playlist::raise_region (boost::shared_ptr<Region> region)
2514 set_layer (region, region->layer() + 1.5);
2519 Playlist::lower_region (boost::shared_ptr<Region> region)
2521 set_layer (region, region->layer() - 1.5);
2526 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2528 set_layer (region, DBL_MAX);
2533 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2535 set_layer (region, -0.5);
2540 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2542 RegionList::iterator i;
2548 RegionLock rlock (const_cast<Playlist *> (this));
2550 for (i = regions.begin(); i != regions.end(); ++i) {
2552 if ((*i)->position() >= start) {
2558 if ((*i)->last_frame() > max_framepos - distance) {
2559 new_pos = max_framepos - (*i)->length();
2561 new_pos = (*i)->position() + distance;
2566 if ((*i)->position() > distance) {
2567 new_pos = (*i)->position() - distance;
2573 (*i)->set_position (new_pos);
2581 notify_contents_changed ();
2587 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2589 RegionLock rlock (const_cast<Playlist*> (this));
2591 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2592 if ((*r)->uses_source (src)) {
2600 boost::shared_ptr<Region>
2601 Playlist::find_region (const ID& id) const
2603 RegionLock rlock (const_cast<Playlist*> (this));
2605 /* searches all regions currently in use by the playlist */
2607 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2608 if ((*i)->id() == id) {
2613 return boost::shared_ptr<Region> ();
2617 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2619 RegionLock rlock (const_cast<Playlist*> (this));
2622 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2631 boost::shared_ptr<Region>
2632 Playlist::region_by_id (const ID& id) const
2634 /* searches all regions ever added to this playlist */
2636 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2637 if ((*i)->id() == id) {
2641 return boost::shared_ptr<Region> ();
2645 Playlist::dump () const
2647 boost::shared_ptr<Region> r;
2649 cerr << "Playlist \"" << _name << "\" " << endl
2650 << regions.size() << " regions "
2653 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2655 cerr << " " << r->name() << " ["
2656 << r->start() << "+" << r->length()
2666 Playlist::set_frozen (bool yn)
2672 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2676 if (region->locked()) {
2683 RegionLock rlock (const_cast<Playlist*> (this));
2688 RegionList::iterator next;
2690 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2691 if ((*i) == region) {
2695 if (next != regions.end()) {
2697 if ((*next)->locked()) {
2703 if ((*next)->position() != region->last_frame() + 1) {
2704 /* they didn't used to touch, so after shuffle,
2705 just have them swap positions.
2707 new_pos = (*next)->position();
2709 /* they used to touch, so after shuffle,
2710 make sure they still do. put the earlier
2711 region where the later one will end after
2714 new_pos = region->position() + (*next)->length();
2717 (*next)->set_position (region->position());
2718 region->set_position (new_pos);
2720 /* avoid a full sort */
2722 regions.erase (i); // removes the region from the list */
2724 regions.insert (next, region); // adds it back after next
2733 RegionList::iterator prev = regions.end();
2735 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2736 if ((*i) == region) {
2738 if (prev != regions.end()) {
2740 if ((*prev)->locked()) {
2745 if (region->position() != (*prev)->last_frame() + 1) {
2746 /* they didn't used to touch, so after shuffle,
2747 just have them swap positions.
2749 new_pos = region->position();
2751 /* they used to touch, so after shuffle,
2752 make sure they still do. put the earlier
2753 one where the later one will end after
2755 new_pos = (*prev)->position() + region->length();
2758 region->set_position ((*prev)->position());
2759 (*prev)->set_position (new_pos);
2761 /* avoid a full sort */
2763 regions.erase (i); // remove region
2764 regions.insert (prev, region); // insert region before prev
2780 check_dependents (region, false);
2782 notify_contents_changed();
2788 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2790 RegionLock rlock (const_cast<Playlist*> (this));
2792 if (regions.size() > 1) {
2800 Playlist::update_after_tempo_map_change ()
2802 RegionLock rlock (const_cast<Playlist*> (this));
2803 RegionList copy (regions.rlist());
2807 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2808 (*i)->update_after_tempo_map_change ();
2815 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2817 RegionLock rl (this, false);
2818 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2824 Playlist::has_region_at (framepos_t const p) const
2826 RegionLock (const_cast<Playlist *> (this));
2828 RegionList::const_iterator i = regions.begin ();
2829 while (i != regions.end() && !(*i)->covers (p)) {
2833 return (i != regions.end());
2836 /** Remove any region that uses a given source */
2838 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2840 RegionLock rl (this);
2842 RegionList::iterator i = regions.begin();
2843 while (i != regions.end()) {
2844 RegionList::iterator j = i;
2847 if ((*i)->uses_source (s)) {
2848 remove_region_internal (*i);
2855 /** Look from a session frame time and find the start time of the next region
2856 * which is on the top layer of this playlist.
2857 * @param t Time to look from.
2858 * @return Position of next top-layered region, or max_framepos if there isn't one.
2861 Playlist::find_next_top_layer_position (framepos_t t) const
2863 RegionLock rlock (const_cast<Playlist *> (this));
2865 layer_t const top = top_layer ();
2867 RegionList copy = regions.rlist ();
2868 copy.sort (RegionSortByPosition ());
2870 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2871 if ((*i)->position() >= t && (*i)->layer() == top) {
2872 return (*i)->position();
2876 return max_framepos;
2879 boost::shared_ptr<Region>
2880 Playlist::combine (const RegionList& r)
2883 uint32_t channels = 0;
2885 framepos_t earliest_position = max_framepos;
2886 vector<TwoRegions> old_and_new_regions;
2887 vector<boost::shared_ptr<Region> > originals;
2888 vector<boost::shared_ptr<Region> > copies;
2891 uint32_t max_level = 0;
2893 /* find the maximum depth of all the regions we're combining */
2895 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2896 max_level = max (max_level, (*i)->max_source_level());
2899 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2900 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2902 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2904 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2905 earliest_position = min (earliest_position, (*i)->position());
2908 /* enable this so that we do not try to create xfades etc. as we add
2912 pl->in_partition = true;
2914 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2916 /* copy the region */
2918 boost::shared_ptr<Region> original_region = (*i);
2919 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2921 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2922 originals.push_back (original_region);
2923 copies.push_back (copied_region);
2925 RegionFactory::add_compound_association (original_region, copied_region);
2927 /* make position relative to zero */
2929 pl->add_region (copied_region, original_region->position() - earliest_position);
2931 /* use the maximum number of channels for any region */
2933 channels = max (channels, original_region->n_channels());
2935 /* it will go above the layer of the highest existing region */
2937 layer = max (layer, original_region->layer());
2940 pl->in_partition = false;
2942 pre_combine (copies);
2944 /* add any dependent regions to the new playlist */
2946 copy_dependents (old_and_new_regions, pl.get());
2948 /* now create a new PlaylistSource for each channel in the new playlist */
2951 pair<framepos_t,framepos_t> extent = pl->get_extent();
2953 for (uint32_t chn = 0; chn < channels; ++chn) {
2954 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2958 /* now a new whole-file region using the list of sources */
2960 plist.add (Properties::start, 0);
2961 plist.add (Properties::length, extent.second);
2962 plist.add (Properties::name, parent_name);
2963 plist.add (Properties::whole_file, true);
2965 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2967 /* now the non-whole-file region that we will actually use in the
2972 plist.add (Properties::start, 0);
2973 plist.add (Properties::length, extent.second);
2974 plist.add (Properties::name, child_name);
2975 plist.add (Properties::layer, layer+1);
2977 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2979 /* remove all the selected regions from the current playlist
2984 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2988 /* do type-specific stuff with the originals and the new compound
2992 post_combine (originals, compound_region);
2994 /* add the new region at the right location */
2996 add_region (compound_region, earliest_position);
3002 return compound_region;
3006 Playlist::uncombine (boost::shared_ptr<Region> target)
3008 boost::shared_ptr<PlaylistSource> pls;
3009 boost::shared_ptr<const Playlist> pl;
3010 vector<boost::shared_ptr<Region> > originals;
3011 vector<TwoRegions> old_and_new_regions;
3013 // (1) check that its really a compound region
3015 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3019 pl = pls->playlist();
3021 framepos_t adjusted_start = 0; // gcc isn't smart enough
3022 framepos_t adjusted_end = 0; // gcc isn't smart enough
3024 /* the leftmost (earliest) edge of the compound region
3025 starts at zero in its source, or larger if it
3026 has been trimmed or content-scrolled.
3028 the rightmost (latest) edge of the compound region
3029 relative to its source is the starting point plus
3030 the length of the region.
3033 // (2) get all the original regions
3035 const RegionList& rl (pl->region_list().rlist());
3036 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3037 frameoffset_t move_offset = 0;
3039 /* there are two possibilities here:
3040 1) the playlist that the playlist source was based on
3041 is us, so just add the originals (which belonged to
3042 us anyway) back in the right place.
3044 2) the playlist that the playlist source was based on
3045 is NOT us, so we need to make copies of each of
3046 the original regions that we find, and add them
3049 bool same_playlist = (pls->original() == id());
3051 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3053 boost::shared_ptr<Region> current (*i);
3055 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3057 if (ca == cassocs.end()) {
3061 boost::shared_ptr<Region> original (ca->second);
3062 bool modified_region;
3064 if (i == rl.begin()) {
3065 move_offset = (target->position() - original->position()) - target->start();
3066 adjusted_start = original->position() + target->start();
3067 adjusted_end = adjusted_start + target->length();
3070 if (!same_playlist) {
3071 framepos_t pos = original->position();
3072 /* make a copy, but don't announce it */
3073 original = RegionFactory::create (original, false);
3074 /* the pure copy constructor resets position() to zero,
3077 original->set_position (pos);
3080 /* check to see how the original region (in the
3081 * playlist before compounding occured) overlaps
3082 * with the new state of the compound region.
3085 original->clear_changes ();
3086 modified_region = false;
3088 switch (original->coverage (adjusted_start, adjusted_end)) {
3090 /* original region does not cover any part
3091 of the current state of the compound region
3095 case OverlapInternal:
3096 /* overlap is just a small piece inside the
3097 * original so trim both ends
3099 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3100 modified_region = true;
3103 case OverlapExternal:
3104 /* overlap fully covers original, so leave it
3110 /* overlap starts within but covers end,
3111 so trim the front of the region
3113 original->trim_front (adjusted_start);
3114 modified_region = true;
3118 /* overlap covers start but ends within, so
3119 * trim the end of the region.
3121 original->trim_end (adjusted_end);
3122 modified_region = true;
3127 /* fix the position to match any movement of the compound region.
3129 original->set_position (original->position() + move_offset);
3130 modified_region = true;
3133 if (modified_region) {
3134 _session.add_command (new StatefulDiffCommand (original));
3137 /* and add to the list of regions waiting to be
3141 originals.push_back (original);
3142 old_and_new_regions.push_back (TwoRegions (*i, original));
3145 pre_uncombine (originals, target);
3147 in_partition = true;
3150 // (3) remove the compound region
3152 remove_region (target);
3154 // (4) add the constituent regions
3156 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3157 add_region ((*i), (*i)->position());
3160 /* now move dependent regions back from the compound to this playlist */
3162 pl->copy_dependents (old_and_new_regions, this);
3164 in_partition = false;
3169 Playlist::max_source_level () const
3171 RegionLock rlock (const_cast<Playlist *> (this));
3174 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3175 lvl = max (lvl, (*i)->max_source_level());
3183 Playlist::count_joined_regions () const
3185 RegionLock rlock (const_cast<Playlist *> (this));
3188 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3189 if ((*i)->max_source_level() > 0) {
3198 Playlist::set_orig_track_id (const PBD::ID& id)
3200 _orig_track_id = id;
3204 Playlist::highest_layering_index () const
3206 RegionLock rlock (const_cast<Playlist *> (this));
3209 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3210 h = max (h, (*i)->layering_index ());