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 boost::shared_ptr<RegionList>
1638 Playlist::regions_at (framepos_t frame)
1640 RegionLock rlock (this);
1641 return find_regions_at (frame);
1645 Playlist::count_regions_at (framepos_t frame) const
1647 RegionLock rlock (const_cast<Playlist*>(this));
1650 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1651 if ((*i)->covers (frame)) {
1659 boost::shared_ptr<Region>
1660 Playlist::top_region_at (framepos_t frame)
1663 RegionLock rlock (this);
1664 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1665 boost::shared_ptr<Region> region;
1667 if (rlist->size()) {
1668 RegionSortByLayer cmp;
1670 region = rlist->back();
1676 boost::shared_ptr<Region>
1677 Playlist::top_unmuted_region_at (framepos_t frame)
1680 RegionLock rlock (this);
1681 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1683 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1685 RegionList::iterator tmp = i;
1688 if ((*i)->muted()) {
1695 boost::shared_ptr<Region> region;
1697 if (rlist->size()) {
1698 RegionSortByLayer cmp;
1700 region = rlist->back();
1706 boost::shared_ptr<RegionList>
1707 Playlist::regions_to_read (framepos_t start, framepos_t end)
1709 /* Caller must hold lock */
1711 RegionList covering;
1712 set<framepos_t> to_check;
1713 set<boost::shared_ptr<Region> > unique;
1715 to_check.insert (start);
1716 to_check.insert (end);
1718 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1720 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1722 /* find all/any regions that span start+end */
1724 switch ((*i)->coverage (start, end)) {
1728 case OverlapInternal:
1729 covering.push_back (*i);
1730 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1734 to_check.insert ((*i)->position());
1735 if ((*i)->position() != 0) {
1736 to_check.insert ((*i)->position()-1);
1738 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1739 covering.push_back (*i);
1743 to_check.insert ((*i)->last_frame());
1744 to_check.insert ((*i)->last_frame()+1);
1745 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1746 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1747 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1748 covering.push_back (*i);
1751 case OverlapExternal:
1752 covering.push_back (*i);
1753 to_check.insert ((*i)->position());
1754 if ((*i)->position() != 0) {
1755 to_check.insert ((*i)->position()-1);
1757 to_check.insert ((*i)->last_frame());
1758 to_check.insert ((*i)->last_frame()+1);
1759 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1760 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1761 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1765 /* don't go too far */
1767 if ((*i)->position() > end) {
1772 boost::shared_ptr<RegionList> rlist (new RegionList);
1774 /* find all the regions that cover each position .... */
1776 if (covering.size() == 1) {
1778 rlist->push_back (covering.front());
1779 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1784 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1788 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1790 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1792 if ((*x)->covers (*t)) {
1793 here.push_back (*x);
1794 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1798 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1805 RegionSortByLayer cmp;
1808 /* ... and get the top/transparent regions at "here" */
1810 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1814 if ((*c)->opaque()) {
1816 /* the other regions at this position are hidden by this one */
1817 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1824 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1825 rlist->push_back (*s);
1828 if (rlist->size() > 1) {
1829 /* now sort by time order */
1831 RegionSortByPosition cmp;
1836 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1841 boost::shared_ptr<RegionList>
1842 Playlist::find_regions_at (framepos_t frame)
1844 /* Caller must hold lock */
1846 boost::shared_ptr<RegionList> rlist (new RegionList);
1848 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1849 if ((*i)->covers (frame)) {
1850 rlist->push_back (*i);
1857 boost::shared_ptr<RegionList>
1858 Playlist::regions_touched (framepos_t start, framepos_t end)
1860 RegionLock rlock (this);
1861 boost::shared_ptr<RegionList> rlist (new RegionList);
1863 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1864 if ((*i)->coverage (start, end) != OverlapNone) {
1865 rlist->push_back (*i);
1873 Playlist::find_next_transient (framepos_t from, int dir)
1875 RegionLock rlock (this);
1876 AnalysisFeatureList points;
1877 AnalysisFeatureList these_points;
1879 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1881 if ((*i)->last_frame() < from) {
1885 if ((*i)->first_frame() > from) {
1890 (*i)->get_transients (these_points);
1892 /* add first frame, just, err, because */
1894 these_points.push_back ((*i)->first_frame());
1896 points.insert (points.end(), these_points.begin(), these_points.end());
1897 these_points.clear ();
1900 if (points.empty()) {
1904 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1905 bool reached = false;
1908 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1913 if (reached && (*x) > from) {
1918 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1923 if (reached && (*x) < from) {
1932 boost::shared_ptr<Region>
1933 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1935 RegionLock rlock (this);
1936 boost::shared_ptr<Region> ret;
1937 framepos_t closest = max_framepos;
1939 bool end_iter = false;
1941 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1945 frameoffset_t distance;
1946 boost::shared_ptr<Region> r = (*i);
1951 pos = r->first_frame ();
1954 pos = r->last_frame ();
1957 pos = r->sync_position ();
1962 case 1: /* forwards */
1965 if ((distance = pos - frame) < closest) {
1974 default: /* backwards */
1977 if ((distance = frame - pos) < closest) {
1994 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1996 RegionLock rlock (this);
1998 framepos_t closest = max_framepos;
1999 framepos_t ret = -1;
2003 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2005 boost::shared_ptr<Region> r = (*i);
2006 frameoffset_t distance;
2008 if (r->first_frame() > frame) {
2010 distance = r->first_frame() - frame;
2012 if (distance < closest) {
2013 ret = r->first_frame();
2018 if (r->last_frame () > frame) {
2020 distance = r->last_frame () - frame;
2022 if (distance < closest) {
2023 ret = r->last_frame ();
2031 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2033 boost::shared_ptr<Region> r = (*i);
2034 frameoffset_t distance;
2036 if (r->last_frame() < frame) {
2038 distance = frame - r->last_frame();
2040 if (distance < closest) {
2041 ret = r->last_frame();
2046 if (r->first_frame() < frame) {
2048 distance = frame - r->first_frame();
2050 if (distance < closest) {
2051 ret = r->first_frame();
2062 /***********************************************************************/
2068 Playlist::mark_session_dirty ()
2070 if (!in_set_state && !holding_state ()) {
2071 _session.set_dirty();
2076 Playlist::rdiff (vector<Command*>& cmds) const
2078 RegionLock rlock (const_cast<Playlist *> (this));
2079 Stateful::rdiff (cmds);
2083 Playlist::clear_owned_changes ()
2085 RegionLock rlock (this);
2086 Stateful::clear_owned_changes ();
2090 Playlist::update (const RegionListProperty::ChangeRecord& change)
2092 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2093 name(), change.added.size(), change.removed.size()));
2096 /* add the added regions */
2097 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2098 add_region_internal ((*i), (*i)->position());
2100 /* remove the removed regions */
2101 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2109 Playlist::set_state (const XMLNode& node, int version)
2113 XMLNodeConstIterator niter;
2114 XMLPropertyList plist;
2115 XMLPropertyConstIterator piter;
2117 boost::shared_ptr<Region> region;
2119 bool seen_region_nodes = false;
2124 if (node.name() != "Playlist") {
2131 plist = node.properties();
2135 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2139 if (prop->name() == X_("name")) {
2140 _name = prop->value();
2142 } else if (prop->name() == X_("orig-diskstream-id")) {
2143 /* XXX legacy session: fix up later */
2144 _orig_track_id = prop->value ();
2145 } else if (prop->name() == X_("orig-track-id")) {
2146 _orig_track_id = prop->value ();
2147 } else if (prop->name() == X_("frozen")) {
2148 _frozen = string_is_affirmative (prop->value());
2149 } else if (prop->name() == X_("combine-ops")) {
2150 _combine_ops = atoi (prop->value());
2156 nlist = node.children();
2158 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2162 if (child->name() == "Region") {
2164 seen_region_nodes = true;
2166 if ((prop = child->property ("id")) == 0) {
2167 error << _("region state node has no ID, ignored") << endmsg;
2171 ID id = prop->value ();
2173 if ((region = region_by_id (id))) {
2175 region->suspend_property_changes ();
2177 if (region->set_state (*child, version)) {
2178 region->resume_property_changes ();
2182 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2183 region->suspend_property_changes ();
2185 error << _("Playlist: cannot create region from XML") << endmsg;
2190 RegionLock rlock (this);
2191 add_region_internal (region, region->position());
2194 region->resume_property_changes ();
2199 if (seen_region_nodes && regions.empty()) {
2203 /* update dependents, which was not done during add_region_internal
2204 due to in_set_state being true
2207 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2208 check_dependents (*r, false);
2213 notify_contents_changed ();
2216 first_set_state = false;
2222 Playlist::get_state()
2224 return state (true);
2228 Playlist::get_template()
2230 return state (false);
2233 /** @param full_state true to include regions in the returned state, otherwise false.
2236 Playlist::state (bool full_state)
2238 XMLNode *node = new XMLNode (X_("Playlist"));
2241 node->add_property (X_("id"), id().to_s());
2242 node->add_property (X_("name"), _name);
2243 node->add_property (X_("type"), _type.to_string());
2245 _orig_track_id.print (buf, sizeof (buf));
2246 node->add_property (X_("orig-track-id"), buf);
2247 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2250 RegionLock rlock (this, false);
2252 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2253 node->add_property ("combine-ops", buf);
2255 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2256 node->add_child_nocopy ((*i)->get_state());
2261 node->add_child_copy (*_extra_xml);
2268 Playlist::empty() const
2270 RegionLock rlock (const_cast<Playlist *>(this), false);
2271 return regions.empty();
2275 Playlist::n_regions() const
2277 RegionLock rlock (const_cast<Playlist *>(this), false);
2278 return regions.size();
2281 pair<framepos_t, framepos_t>
2282 Playlist::get_extent () const
2284 RegionLock rlock (const_cast<Playlist *>(this), false);
2285 return _get_extent ();
2288 pair<framepos_t, framepos_t>
2289 Playlist::_get_extent () const
2291 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2293 if (regions.empty()) {
2298 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2299 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2300 if (e.first < ext.first) {
2301 ext.first = e.first;
2303 if (e.second > ext.second) {
2304 ext.second = e.second;
2312 Playlist::bump_name (string name, Session &session)
2314 string newname = name;
2317 newname = bump_name_once (newname, '.');
2318 } while (session.playlists->by_name (newname)!=NULL);
2325 Playlist::top_layer() const
2327 RegionLock rlock (const_cast<Playlist *> (this));
2330 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2331 top = max (top, (*i)->layer());
2337 Playlist::set_edit_mode (EditMode mode)
2342 struct RelayerSort {
2343 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2344 return a->layering_index() < b->layering_index();
2348 /** Set a new layer for a region. This adjusts the layering indices of all
2349 * regions in the playlist to put the specified region in the appropriate
2350 * place. The actual layering will be fixed up when relayer() happens.
2354 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2356 /* Remove the layer we are setting from our region list, and sort it */
2357 RegionList copy = regions.rlist();
2358 copy.remove (region);
2359 copy.sort (RelayerSort ());
2361 /* Put region back in the right place */
2362 RegionList::iterator i = copy.begin();
2363 while (i != copy.end ()) {
2364 if ((*i)->layer() > new_layer) {
2370 copy.insert (i, region);
2372 setup_layering_indices (copy);
2376 Playlist::setup_layering_indices (RegionList const & regions) const
2379 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2380 (*k)->set_layering_index (j++);
2385 /** Take the layering indices of each of our regions, compute the layers
2386 * that they should be on, and write the layers back to the regions.
2389 Playlist::relayer ()
2391 /* never compute layers when setting from XML */
2397 /* Build up a new list of regions on each layer, stored in a set of lists
2398 each of which represent some period of time on some layer. The idea
2399 is to avoid having to search the entire region list to establish whether
2400 each region overlaps another */
2402 /* how many pieces to divide this playlist's time up into */
2403 int const divisions = 512;
2405 /* find the start and end positions of the regions on this playlist */
2406 framepos_t start = INT64_MAX;
2408 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2409 start = min (start, (*i)->position());
2410 end = max (end, (*i)->position() + (*i)->length());
2413 /* hence the size of each time division */
2414 double const division_size = (end - start) / double (divisions);
2416 vector<vector<RegionList> > layers;
2417 layers.push_back (vector<RegionList> (divisions));
2419 /* Sort our regions into layering index order */
2420 RegionList copy = regions.rlist();
2421 copy.sort (RelayerSort ());
2423 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2424 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2425 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2428 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2430 /* find the time divisions that this region covers; if there are no regions on the list,
2431 division_size will equal 0 and in this case we'll just say that
2432 start_division = end_division = 0.
2434 int start_division = 0;
2435 int end_division = 0;
2437 if (division_size > 0) {
2438 start_division = floor ( ((*i)->position() - start) / division_size);
2439 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2440 if (end_division == divisions) {
2445 assert (divisions == 0 || end_division < divisions);
2447 /* find the lowest layer that this region can go on */
2448 size_t j = layers.size();
2450 /* try layer j - 1; it can go on if it overlaps no other region
2451 that is already on that layer
2454 bool overlap = false;
2455 for (int k = start_division; k <= end_division; ++k) {
2456 RegionList::iterator l = layers[j-1][k].begin ();
2457 while (l != layers[j-1][k].end()) {
2458 if ((*l)->overlap_equivalent (*i)) {
2471 /* overlap, so we must use layer j */
2478 if (j == layers.size()) {
2479 /* we need a new layer for this region */
2480 layers.push_back (vector<RegionList> (divisions));
2483 /* put a reference to this region in each of the divisions that it exists in */
2484 for (int k = start_division; k <= end_division; ++k) {
2485 layers[j][k].push_back (*i);
2488 (*i)->set_layer (j);
2491 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2492 relayering because we just removed the only region on the top layer, nothing will
2493 appear to have changed, but the StreamView must still sort itself out. We could
2494 probably keep a note of the top layer last time we relayered, and check that,
2495 but premature optimisation &c...
2497 notify_layering_changed ();
2499 /* This relayer() may have been called as a result of a region removal, in which
2500 case we need to setup layering indices so account for the one that has just
2503 setup_layering_indices (copy);
2507 Playlist::raise_region (boost::shared_ptr<Region> region)
2509 set_layer (region, region->layer() + 1.5);
2514 Playlist::lower_region (boost::shared_ptr<Region> region)
2516 set_layer (region, region->layer() - 1.5);
2521 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2523 set_layer (region, DBL_MAX);
2528 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2530 set_layer (region, -0.5);
2535 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2537 RegionList::iterator i;
2543 RegionLock rlock (const_cast<Playlist *> (this));
2545 for (i = regions.begin(); i != regions.end(); ++i) {
2547 if ((*i)->position() >= start) {
2553 if ((*i)->last_frame() > max_framepos - distance) {
2554 new_pos = max_framepos - (*i)->length();
2556 new_pos = (*i)->position() + distance;
2561 if ((*i)->position() > distance) {
2562 new_pos = (*i)->position() - distance;
2568 (*i)->set_position (new_pos);
2576 notify_contents_changed ();
2582 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2584 RegionLock rlock (const_cast<Playlist*> (this));
2586 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2587 if ((*r)->uses_source (src)) {
2595 boost::shared_ptr<Region>
2596 Playlist::find_region (const ID& id) const
2598 RegionLock rlock (const_cast<Playlist*> (this));
2600 /* searches all regions currently in use by the playlist */
2602 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2603 if ((*i)->id() == id) {
2608 return boost::shared_ptr<Region> ();
2612 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2614 RegionLock rlock (const_cast<Playlist*> (this));
2617 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2626 boost::shared_ptr<Region>
2627 Playlist::region_by_id (const ID& id) const
2629 /* searches all regions ever added to this playlist */
2631 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2632 if ((*i)->id() == id) {
2636 return boost::shared_ptr<Region> ();
2640 Playlist::dump () const
2642 boost::shared_ptr<Region> r;
2644 cerr << "Playlist \"" << _name << "\" " << endl
2645 << regions.size() << " regions "
2648 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2650 cerr << " " << r->name() << " ["
2651 << r->start() << "+" << r->length()
2661 Playlist::set_frozen (bool yn)
2667 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2671 if (region->locked()) {
2678 RegionLock rlock (const_cast<Playlist*> (this));
2683 RegionList::iterator next;
2685 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2686 if ((*i) == region) {
2690 if (next != regions.end()) {
2692 if ((*next)->locked()) {
2698 if ((*next)->position() != region->last_frame() + 1) {
2699 /* they didn't used to touch, so after shuffle,
2700 just have them swap positions.
2702 new_pos = (*next)->position();
2704 /* they used to touch, so after shuffle,
2705 make sure they still do. put the earlier
2706 region where the later one will end after
2709 new_pos = region->position() + (*next)->length();
2712 (*next)->set_position (region->position());
2713 region->set_position (new_pos);
2715 /* avoid a full sort */
2717 regions.erase (i); // removes the region from the list */
2719 regions.insert (next, region); // adds it back after next
2728 RegionList::iterator prev = regions.end();
2730 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2731 if ((*i) == region) {
2733 if (prev != regions.end()) {
2735 if ((*prev)->locked()) {
2740 if (region->position() != (*prev)->last_frame() + 1) {
2741 /* they didn't used to touch, so after shuffle,
2742 just have them swap positions.
2744 new_pos = region->position();
2746 /* they used to touch, so after shuffle,
2747 make sure they still do. put the earlier
2748 one where the later one will end after
2750 new_pos = (*prev)->position() + region->length();
2753 region->set_position ((*prev)->position());
2754 (*prev)->set_position (new_pos);
2756 /* avoid a full sort */
2758 regions.erase (i); // remove region
2759 regions.insert (prev, region); // insert region before prev
2775 check_dependents (region, false);
2777 notify_contents_changed();
2783 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2785 RegionLock rlock (const_cast<Playlist*> (this));
2787 if (regions.size() > 1) {
2795 Playlist::update_after_tempo_map_change ()
2797 RegionLock rlock (const_cast<Playlist*> (this));
2798 RegionList copy (regions.rlist());
2802 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2803 (*i)->update_after_tempo_map_change ();
2810 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2812 RegionLock rl (this, false);
2813 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2819 Playlist::has_region_at (framepos_t const p) const
2821 RegionLock (const_cast<Playlist *> (this));
2823 RegionList::const_iterator i = regions.begin ();
2824 while (i != regions.end() && !(*i)->covers (p)) {
2828 return (i != regions.end());
2831 /** Remove any region that uses a given source */
2833 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2835 RegionLock rl (this);
2837 RegionList::iterator i = regions.begin();
2838 while (i != regions.end()) {
2839 RegionList::iterator j = i;
2842 if ((*i)->uses_source (s)) {
2843 remove_region_internal (*i);
2850 /** Look from a session frame time and find the start time of the next region
2851 * which is on the top layer of this playlist.
2852 * @param t Time to look from.
2853 * @return Position of next top-layered region, or max_framepos if there isn't one.
2856 Playlist::find_next_top_layer_position (framepos_t t) const
2858 RegionLock rlock (const_cast<Playlist *> (this));
2860 layer_t const top = top_layer ();
2862 RegionList copy = regions.rlist ();
2863 copy.sort (RegionSortByPosition ());
2865 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2866 if ((*i)->position() >= t && (*i)->layer() == top) {
2867 return (*i)->position();
2871 return max_framepos;
2874 boost::shared_ptr<Region>
2875 Playlist::combine (const RegionList& r)
2878 uint32_t channels = 0;
2880 framepos_t earliest_position = max_framepos;
2881 vector<TwoRegions> old_and_new_regions;
2882 vector<boost::shared_ptr<Region> > originals;
2883 vector<boost::shared_ptr<Region> > copies;
2886 uint32_t max_level = 0;
2888 /* find the maximum depth of all the regions we're combining */
2890 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2891 max_level = max (max_level, (*i)->max_source_level());
2894 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2895 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2897 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2899 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2900 earliest_position = min (earliest_position, (*i)->position());
2903 /* enable this so that we do not try to create xfades etc. as we add
2907 pl->in_partition = true;
2909 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2911 /* copy the region */
2913 boost::shared_ptr<Region> original_region = (*i);
2914 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2916 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2917 originals.push_back (original_region);
2918 copies.push_back (copied_region);
2920 RegionFactory::add_compound_association (original_region, copied_region);
2922 /* make position relative to zero */
2924 pl->add_region (copied_region, original_region->position() - earliest_position);
2926 /* use the maximum number of channels for any region */
2928 channels = max (channels, original_region->n_channels());
2930 /* it will go above the layer of the highest existing region */
2932 layer = max (layer, original_region->layer());
2935 pl->in_partition = false;
2937 pre_combine (copies);
2939 /* add any dependent regions to the new playlist */
2941 copy_dependents (old_and_new_regions, pl.get());
2943 /* now create a new PlaylistSource for each channel in the new playlist */
2946 pair<framepos_t,framepos_t> extent = pl->get_extent();
2948 for (uint32_t chn = 0; chn < channels; ++chn) {
2949 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2953 /* now a new whole-file region using the list of sources */
2955 plist.add (Properties::start, 0);
2956 plist.add (Properties::length, extent.second);
2957 plist.add (Properties::name, parent_name);
2958 plist.add (Properties::whole_file, true);
2960 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2962 /* now the non-whole-file region that we will actually use in the
2967 plist.add (Properties::start, 0);
2968 plist.add (Properties::length, extent.second);
2969 plist.add (Properties::name, child_name);
2970 plist.add (Properties::layer, layer+1);
2972 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2974 /* remove all the selected regions from the current playlist
2979 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2983 /* do type-specific stuff with the originals and the new compound
2987 post_combine (originals, compound_region);
2989 /* add the new region at the right location */
2991 add_region (compound_region, earliest_position);
2997 return compound_region;
3001 Playlist::uncombine (boost::shared_ptr<Region> target)
3003 boost::shared_ptr<PlaylistSource> pls;
3004 boost::shared_ptr<const Playlist> pl;
3005 vector<boost::shared_ptr<Region> > originals;
3006 vector<TwoRegions> old_and_new_regions;
3008 // (1) check that its really a compound region
3010 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3014 pl = pls->playlist();
3016 framepos_t adjusted_start = 0; // gcc isn't smart enough
3017 framepos_t adjusted_end = 0; // gcc isn't smart enough
3019 /* the leftmost (earliest) edge of the compound region
3020 starts at zero in its source, or larger if it
3021 has been trimmed or content-scrolled.
3023 the rightmost (latest) edge of the compound region
3024 relative to its source is the starting point plus
3025 the length of the region.
3028 // (2) get all the original regions
3030 const RegionList& rl (pl->region_list().rlist());
3031 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3032 frameoffset_t move_offset = 0;
3034 /* there are two possibilities here:
3035 1) the playlist that the playlist source was based on
3036 is us, so just add the originals (which belonged to
3037 us anyway) back in the right place.
3039 2) the playlist that the playlist source was based on
3040 is NOT us, so we need to make copies of each of
3041 the original regions that we find, and add them
3044 bool same_playlist = (pls->original() == id());
3046 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3048 boost::shared_ptr<Region> current (*i);
3050 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3052 if (ca == cassocs.end()) {
3056 boost::shared_ptr<Region> original (ca->second);
3057 bool modified_region;
3059 if (i == rl.begin()) {
3060 move_offset = (target->position() - original->position()) - target->start();
3061 adjusted_start = original->position() + target->start();
3062 adjusted_end = adjusted_start + target->length();
3065 if (!same_playlist) {
3066 framepos_t pos = original->position();
3067 /* make a copy, but don't announce it */
3068 original = RegionFactory::create (original, false);
3069 /* the pure copy constructor resets position() to zero,
3072 original->set_position (pos);
3075 /* check to see how the original region (in the
3076 * playlist before compounding occured) overlaps
3077 * with the new state of the compound region.
3080 original->clear_changes ();
3081 modified_region = false;
3083 switch (original->coverage (adjusted_start, adjusted_end)) {
3085 /* original region does not cover any part
3086 of the current state of the compound region
3090 case OverlapInternal:
3091 /* overlap is just a small piece inside the
3092 * original so trim both ends
3094 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3095 modified_region = true;
3098 case OverlapExternal:
3099 /* overlap fully covers original, so leave it
3105 /* overlap starts within but covers end,
3106 so trim the front of the region
3108 original->trim_front (adjusted_start);
3109 modified_region = true;
3113 /* overlap covers start but ends within, so
3114 * trim the end of the region.
3116 original->trim_end (adjusted_end);
3117 modified_region = true;
3122 /* fix the position to match any movement of the compound region.
3124 original->set_position (original->position() + move_offset);
3125 modified_region = true;
3128 if (modified_region) {
3129 _session.add_command (new StatefulDiffCommand (original));
3132 /* and add to the list of regions waiting to be
3136 originals.push_back (original);
3137 old_and_new_regions.push_back (TwoRegions (*i, original));
3140 pre_uncombine (originals, target);
3142 in_partition = true;
3145 // (3) remove the compound region
3147 remove_region (target);
3149 // (4) add the constituent regions
3151 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3152 add_region ((*i), (*i)->position());
3155 /* now move dependent regions back from the compound to this playlist */
3157 pl->copy_dependents (old_and_new_regions, this);
3159 in_partition = false;
3164 Playlist::max_source_level () const
3166 RegionLock rlock (const_cast<Playlist *> (this));
3169 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3170 lvl = max (lvl, (*i)->max_source_level());
3177 Playlist::set_orig_track_id (const PBD::ID& id)
3179 _orig_track_id = id;