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;
190 layer_op_counter = other->layer_op_counter;
191 freeze_length = other->freeze_length;
194 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
195 : SessionObject(other->_session, str)
197 , _type(other->_type)
198 , _orig_track_id (other->_orig_track_id)
200 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
202 framepos_t end = start + cnt - 1;
208 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
210 boost::shared_ptr<Region> region;
211 boost::shared_ptr<Region> new_region;
212 frameoffset_t offset = 0;
213 framepos_t position = 0;
220 overlap = region->coverage (start, end);
226 case OverlapInternal:
227 offset = start - region->position();
234 position = region->position() - start;
235 len = end - region->position();
239 offset = start - region->position();
241 len = region->length() - offset;
244 case OverlapExternal:
246 position = region->position() - start;
247 len = region->length();
251 RegionFactory::region_name (new_name, region->name(), false);
255 plist.add (Properties::start, region->start() + offset);
256 plist.add (Properties::length, len);
257 plist.add (Properties::name, new_name);
258 plist.add (Properties::layer, region->layer());
260 new_region = RegionFactory::RegionFactory::create (region, plist);
262 add_region_internal (new_region, position);
266 first_set_state = false;
273 InUse (true); /* EMIT SIGNAL */
284 InUse (false); /* EMIT SIGNAL */
289 Playlist::copy_regions (RegionList& newlist) const
291 RegionLock rlock (const_cast<Playlist *> (this));
293 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
294 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
299 Playlist::init (bool hide)
301 add_property (regions);
302 _xml_node_name = X_("Playlist");
304 g_atomic_int_set (&block_notifications, 0);
305 g_atomic_int_set (&ignore_state_changes, 0);
306 pending_contents_change = false;
307 pending_layering = false;
308 first_set_state = true;
316 _edit_mode = Config->get_edit_mode();
318 in_partition = false;
321 layer_op_counter = 0;
323 _explicit_relayering = false;
326 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
327 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
329 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
332 Playlist::~Playlist ()
334 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
337 RegionLock rl (this);
339 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
340 (*i)->set_playlist (boost::shared_ptr<Playlist>());
344 /* GoingAway must be emitted by derived classes */
348 Playlist::_set_sort_id ()
351 Playlists are given names like <track name>.<id>
352 or <track name>.<edit group name>.<id> where id
353 is an integer. We extract the id and sort by that.
356 size_t dot_position = _name.val().find_last_of(".");
358 if (dot_position == string::npos) {
361 string t = _name.val().substr(dot_position + 1);
364 _sort_id = boost::lexical_cast<int>(t);
367 catch (boost::bad_lexical_cast e) {
374 Playlist::set_name (const string& str)
376 /* in a typical situation, a playlist is being used
377 by one diskstream and also is referenced by the
378 Session. if there are more references than that,
379 then don't change the name.
386 bool ret = SessionObject::set_name(str);
393 /***********************************************************************
394 CHANGE NOTIFICATION HANDLING
396 Notifications must be delayed till the region_lock is released. This
397 is necessary because handlers for the signals may need to acquire
398 the lock (e.g. to read from the playlist).
399 ***********************************************************************/
402 Playlist::begin_undo ()
409 Playlist::end_undo ()
418 delay_notifications ();
419 g_atomic_int_inc (&ignore_state_changes);
422 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
424 Playlist::thaw (bool from_undo)
426 g_atomic_int_dec_and_test (&ignore_state_changes);
427 release_notifications (from_undo);
432 Playlist::delay_notifications ()
434 g_atomic_int_inc (&block_notifications);
435 freeze_length = _get_extent().second;
438 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
440 Playlist::release_notifications (bool from_undo)
442 if (g_atomic_int_dec_and_test (&block_notifications)) {
443 flush_notifications (from_undo);
448 Playlist::notify_contents_changed ()
450 if (holding_state ()) {
451 pending_contents_change = true;
453 pending_contents_change = false;
454 ContentsChanged(); /* EMIT SIGNAL */
459 Playlist::notify_layering_changed ()
461 if (holding_state ()) {
462 pending_layering = true;
464 pending_layering = false;
465 LayeringChanged(); /* EMIT SIGNAL */
470 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
472 if (holding_state ()) {
473 pending_removes.insert (r);
474 pending_contents_change = true;
476 /* this might not be true, but we have to act
477 as though it could be.
479 pending_contents_change = false;
480 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
481 ContentsChanged (); /* EMIT SIGNAL */
486 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
488 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
490 if (holding_state ()) {
492 pending_range_moves.push_back (move);
496 list< Evoral::RangeMove<framepos_t> > m;
498 RangesMoved (m, false);
504 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
506 if (r->position() >= r->last_position()) {
507 /* trimmed shorter */
511 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
513 if (holding_state ()) {
515 pending_region_extensions.push_back (extra);
519 list<Evoral::Range<framepos_t> > r;
527 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
529 if (r->length() < r->last_length()) {
530 /* trimmed shorter */
533 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
535 if (holding_state ()) {
537 pending_region_extensions.push_back (extra);
541 list<Evoral::Range<framepos_t> > r;
549 Playlist::notify_region_added (boost::shared_ptr<Region> r)
551 /* the length change might not be true, but we have to act
552 as though it could be.
555 if (holding_state()) {
556 pending_adds.insert (r);
557 pending_contents_change = true;
560 pending_contents_change = false;
561 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
562 ContentsChanged (); /* EMIT SIGNAL */
567 Playlist::notify_length_changed ()
569 if (!holding_state ()) {
570 pending_contents_change = false;
571 ContentsChanged (); /* EMIT SIGNAL */
575 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
577 Playlist::flush_notifications (bool from_undo)
579 set<boost::shared_ptr<Region> > dependent_checks_needed;
580 set<boost::shared_ptr<Region> >::iterator s;
581 uint32_t regions_changed = false;
589 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
590 regions_changed = true;
593 /* we have no idea what order the regions ended up in pending
594 bounds (it could be based on selection order, for example).
595 so, to preserve layering in the "most recently moved is higher"
596 model, sort them by existing layer, then timestamp them.
599 // RegionSortByLayer cmp;
600 // pending_bounds.sort (cmp);
602 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
603 if (_session.config.get_layer_model() == MoveAddHigher) {
604 timestamp_layer_op (*r);
606 dependent_checks_needed.insert (*r);
609 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
610 remove_dependents (*s);
611 // cerr << _name << " sends RegionRemoved\n";
612 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
615 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
616 // cerr << _name << " sends RegionAdded\n";
617 /* don't emit RegionAdded signal until relayering is done,
618 so that the region is fully setup by the time
619 anyone hear's that its been added
621 dependent_checks_needed.insert (*s);
624 if (regions_changed || pending_contents_change) {
628 pending_contents_change = false;
629 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
630 ContentsChanged (); /* EMIT SIGNAL */
631 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
634 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
635 (*s)->clear_changes ();
636 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
639 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
640 check_dependents (*s, false);
643 if (!pending_range_moves.empty ()) {
644 RangesMoved (pending_range_moves, from_undo);
647 if (!pending_region_extensions.empty ()) {
648 RegionsExtended (pending_region_extensions);
657 Playlist::clear_pending ()
659 pending_adds.clear ();
660 pending_removes.clear ();
661 pending_bounds.clear ();
662 pending_range_moves.clear ();
663 pending_region_extensions.clear ();
664 pending_contents_change = false;
667 /*************************************************************
669 *************************************************************/
672 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
674 RegionLock rlock (this);
675 times = fabs (times);
677 int itimes = (int) floor (times);
679 framepos_t pos = position;
681 if (times == 1 && auto_partition){
682 partition(pos - 1, (pos + region->length()), true);
686 add_region_internal (region, pos);
687 pos += region->length();
692 /* note that itimes can be zero if we being asked to just
693 insert a single fraction of the region.
696 for (int i = 0; i < itimes; ++i) {
697 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
698 add_region_internal (copy, pos);
699 pos += region->length();
702 framecnt_t length = 0;
704 if (floor (times) != times) {
705 length = (framecnt_t) floor (region->length() * (times - floor (times)));
707 RegionFactory::region_name (name, region->name(), false);
712 plist.add (Properties::start, region->start());
713 plist.add (Properties::length, length);
714 plist.add (Properties::name, name);
715 plist.add (Properties::layer, region->layer());
717 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
718 add_region_internal (sub, pos);
722 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
726 Playlist::set_region_ownership ()
728 RegionLock rl (this);
729 RegionList::iterator i;
730 boost::weak_ptr<Playlist> pl (shared_from_this());
732 for (i = regions.begin(); i != regions.end(); ++i) {
733 (*i)->set_playlist (pl);
738 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
740 if (region->data_type() != _type){
744 RegionSortByPosition cmp;
746 framecnt_t old_length = 0;
748 if (!holding_state()) {
749 old_length = _get_extent().second;
752 if (!first_set_state) {
753 boost::shared_ptr<Playlist> foo (shared_from_this());
754 region->set_playlist (boost::weak_ptr<Playlist>(foo));
757 region->set_position (position);
759 timestamp_layer_op (region);
761 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
762 all_regions.insert (region);
764 possibly_splice_unlocked (position, region->length(), region);
766 if (!holding_state ()) {
767 /* layers get assigned from XML state, and are not reset during undo/redo */
771 /* we need to notify the existence of new region before checking dependents. Ick. */
773 notify_region_added (region);
776 if (!holding_state ()) {
778 check_dependents (region, false);
780 if (old_length != _get_extent().second) {
781 notify_length_changed ();
785 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
791 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
793 RegionLock rlock (this);
795 bool old_sp = _splicing;
798 remove_region_internal (old);
799 add_region_internal (newr, pos);
803 possibly_splice_unlocked (pos, old->length() - newr->length());
807 Playlist::remove_region (boost::shared_ptr<Region> region)
809 RegionLock rlock (this);
810 remove_region_internal (region);
814 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
816 RegionList::iterator i;
817 framecnt_t old_length = 0;
819 if (!holding_state()) {
820 old_length = _get_extent().second;
825 region->set_playlist (boost::weak_ptr<Playlist>());
828 /* XXX should probably freeze here .... */
830 for (i = regions.begin(); i != regions.end(); ++i) {
833 framepos_t pos = (*i)->position();
834 framecnt_t distance = (*i)->length();
838 possibly_splice_unlocked (pos, -distance);
840 if (!holding_state ()) {
842 remove_dependents (region);
844 if (old_length != _get_extent().second) {
845 notify_length_changed ();
849 notify_region_removed (region);
858 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
860 if (Config->get_use_overlap_equivalency()) {
861 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
862 if ((*i)->overlap_equivalent (other)) {
863 results.push_back ((*i));
867 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
868 if ((*i)->equivalent (other)) {
869 results.push_back ((*i));
876 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
878 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
880 if ((*i) && (*i)->region_list_equivalent (other)) {
881 results.push_back (*i);
887 Playlist::partition (framepos_t start, framepos_t end, bool cut)
891 partition_internal (start, end, cut, thawlist);
893 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
894 (*i)->resume_property_changes ();
899 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
901 RegionList new_regions;
904 RegionLock rlock (this);
906 boost::shared_ptr<Region> region;
907 boost::shared_ptr<Region> current;
909 RegionList::iterator tmp;
911 framepos_t pos1, pos2, pos3, pos4;
915 /* need to work from a copy, because otherwise the regions we add during the process
916 get operated on as well.
919 RegionList copy = regions.rlist();
921 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
928 if (current->first_frame() >= start && current->last_frame() < end) {
931 remove_region_internal (current);
937 /* coverage will return OverlapStart if the start coincides
938 with the end point. we do not partition such a region,
939 so catch this special case.
942 if (current->first_frame() >= end) {
946 if ((overlap = current->coverage (start, end)) == OverlapNone) {
950 pos1 = current->position();
953 pos4 = current->last_frame();
955 if (overlap == OverlapInternal) {
956 /* split: we need 3 new regions, the front, middle and end.
957 cut: we need 2 regions, the front and end.
962 ---------------*************************------------
965 ---------------*****++++++++++++++++====------------
967 ---------------*****----------------====------------
972 /* "middle" ++++++ */
974 RegionFactory::region_name (new_name, current->name(), false);
978 plist.add (Properties::start, current->start() + (pos2 - pos1));
979 plist.add (Properties::length, pos3 - pos2);
980 plist.add (Properties::name, new_name);
981 plist.add (Properties::layer, regions.size());
982 plist.add (Properties::automatic, true);
983 plist.add (Properties::left_of_split, true);
984 plist.add (Properties::right_of_split, true);
986 region = RegionFactory::create (current, plist);
987 add_region_internal (region, start);
988 new_regions.push_back (region);
993 RegionFactory::region_name (new_name, current->name(), false);
997 plist.add (Properties::start, current->start() + (pos3 - pos1));
998 plist.add (Properties::length, pos4 - pos3);
999 plist.add (Properties::name, new_name);
1000 plist.add (Properties::layer, regions.size());
1001 plist.add (Properties::automatic, true);
1002 plist.add (Properties::right_of_split, true);
1004 region = RegionFactory::create (current, plist);
1006 add_region_internal (region, end);
1007 new_regions.push_back (region);
1011 current->suspend_property_changes ();
1012 thawlist.push_back (current);
1013 current->cut_end (pos2 - 1);
1015 } else if (overlap == OverlapEnd) {
1019 ---------------*************************------------
1022 ---------------**************+++++++++++------------
1024 ---------------**************-----------------------
1031 RegionFactory::region_name (new_name, current->name(), false);
1035 plist.add (Properties::start, current->start() + (pos2 - pos1));
1036 plist.add (Properties::length, pos4 - pos2);
1037 plist.add (Properties::name, new_name);
1038 plist.add (Properties::layer, regions.size());
1039 plist.add (Properties::automatic, true);
1040 plist.add (Properties::left_of_split, true);
1042 region = RegionFactory::create (current, plist);
1044 add_region_internal (region, start);
1045 new_regions.push_back (region);
1050 current->suspend_property_changes ();
1051 thawlist.push_back (current);
1052 current->cut_end (pos2 - 1);
1054 } else if (overlap == OverlapStart) {
1056 /* split: we need 2 regions: the front and the end.
1057 cut: just trim current to skip the cut area
1062 ---------------*************************------------
1066 ---------------****+++++++++++++++++++++------------
1068 -------------------*********************------------
1074 RegionFactory::region_name (new_name, current->name(), false);
1078 plist.add (Properties::start, current->start());
1079 plist.add (Properties::length, pos3 - pos1);
1080 plist.add (Properties::name, new_name);
1081 plist.add (Properties::layer, regions.size());
1082 plist.add (Properties::automatic, true);
1083 plist.add (Properties::right_of_split, true);
1085 region = RegionFactory::create (current, plist);
1087 add_region_internal (region, pos1);
1088 new_regions.push_back (region);
1093 current->suspend_property_changes ();
1094 thawlist.push_back (current);
1095 current->trim_front (pos3);
1096 } else if (overlap == OverlapExternal) {
1098 /* split: no split required.
1099 cut: remove the region.
1104 ---------------*************************------------
1108 ---------------*************************------------
1110 ----------------------------------------------------
1115 remove_region_internal (current);
1118 new_regions.push_back (current);
1122 in_partition = false;
1125 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1126 check_dependents (*i, false);
1130 boost::shared_ptr<Playlist>
1131 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1133 boost::shared_ptr<Playlist> ret;
1134 boost::shared_ptr<Playlist> pl;
1137 if (ranges.empty()) {
1138 return boost::shared_ptr<Playlist>();
1141 start = ranges.front().start;
1143 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1145 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1147 if (i == ranges.begin()) {
1151 /* paste the next section into the nascent playlist,
1152 offset to reflect the start of the first range we
1156 ret->paste (pl, (*i).start - start, 1.0f);
1163 boost::shared_ptr<Playlist>
1164 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1166 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1167 return cut_copy (pmf, ranges, result_is_hidden);
1170 boost::shared_ptr<Playlist>
1171 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1173 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1174 return cut_copy (pmf, ranges, result_is_hidden);
1177 boost::shared_ptr<Playlist>
1178 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1180 boost::shared_ptr<Playlist> the_copy;
1181 RegionList thawlist;
1184 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1185 string new_name = _name;
1189 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1190 return boost::shared_ptr<Playlist>();
1193 partition_internal (start, start+cnt-1, true, thawlist);
1195 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1196 (*i)->resume_property_changes();
1202 boost::shared_ptr<Playlist>
1203 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1207 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1208 string new_name = _name;
1212 cnt = min (_get_extent().second - start, cnt);
1213 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1217 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1219 times = fabs (times);
1222 RegionLock rl1 (this);
1223 RegionLock rl2 (other.get());
1225 framecnt_t const old_length = _get_extent().second;
1227 int itimes = (int) floor (times);
1228 framepos_t pos = position;
1229 framecnt_t const shift = other->_get_extent().second;
1230 layer_t top_layer = regions.size();
1233 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1234 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1236 /* put these new regions on top of all existing ones, but preserve
1237 the ordering they had in the original playlist.
1240 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1241 add_region_internal (copy_of_region, (*i)->position() + pos);
1247 /* XXX shall we handle fractional cases at some point? */
1249 if (old_length != _get_extent().second) {
1250 notify_length_changed ();
1261 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1263 times = fabs (times);
1265 RegionLock rl (this);
1266 int itimes = (int) floor (times);
1267 framepos_t pos = position + 1;
1270 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1271 add_region_internal (copy, pos);
1272 pos += region->length();
1275 if (floor (times) != times) {
1276 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1278 RegionFactory::region_name (name, region->name(), false);
1283 plist.add (Properties::start, region->start());
1284 plist.add (Properties::length, length);
1285 plist.add (Properties::name, name);
1287 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1288 add_region_internal (sub, pos);
1294 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1296 RegionLock rlock (this);
1297 RegionList copy (regions.rlist());
1300 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1302 if ((*r)->last_frame() < at) {
1307 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1308 /* intersected region */
1309 if (!move_intersected) {
1314 /* do not move regions glued to music time - that
1315 has to be done separately.
1318 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1319 fixup.push_back (*r);
1323 (*r)->set_position ((*r)->position() + distance);
1326 /* XXX: may not be necessary; Region::post_set should do this, I think */
1327 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1328 (*r)->recompute_position_from_lock_style ();
1333 Playlist::split (framepos_t at)
1335 RegionLock rlock (this);
1336 RegionList copy (regions.rlist());
1338 /* use a copy since this operation can modify the region list
1341 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1342 _split_region (*r, at);
1347 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1349 RegionLock rl (this);
1350 _split_region (region, playlist_position);
1354 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1356 if (!region->covers (playlist_position)) {
1360 if (region->position() == playlist_position ||
1361 region->last_frame() == playlist_position) {
1365 boost::shared_ptr<Region> left;
1366 boost::shared_ptr<Region> right;
1367 frameoffset_t before;
1368 frameoffset_t after;
1372 /* split doesn't change anything about length, so don't try to splice */
1374 bool old_sp = _splicing;
1377 before = playlist_position - region->position();
1378 after = region->length() - before;
1380 RegionFactory::region_name (before_name, region->name(), false);
1385 plist.add (Properties::position, region->position ());
1386 plist.add (Properties::length, before);
1387 plist.add (Properties::name, before_name);
1388 plist.add (Properties::left_of_split, true);
1390 /* note: we must use the version of ::create with an offset here,
1391 since it supplies that offset to the Region constructor, which
1392 is necessary to get audio region gain envelopes right.
1394 left = RegionFactory::create (region, 0, plist);
1397 RegionFactory::region_name (after_name, region->name(), false);
1402 plist.add (Properties::position, region->position() + before);
1403 plist.add (Properties::length, after);
1404 plist.add (Properties::name, after_name);
1405 plist.add (Properties::right_of_split, true);
1407 /* same note as above */
1408 right = RegionFactory::create (region, before, plist);
1411 add_region_internal (left, region->position());
1412 add_region_internal (right, region->position() + before);
1414 uint64_t orig_layer_op = region->last_layer_op();
1415 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1416 if ((*i)->last_layer_op() > orig_layer_op) {
1417 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1421 left->set_last_layer_op ( orig_layer_op );
1422 right->set_last_layer_op ( orig_layer_op + 1);
1426 finalize_split_region (region, left, right);
1428 remove_region_internal (region);
1434 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1436 if (_splicing || in_set_state) {
1437 /* don't respond to splicing moves or state setting */
1441 if (_edit_mode == Splice) {
1442 splice_locked (at, distance, exclude);
1447 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1449 if (_splicing || in_set_state) {
1450 /* don't respond to splicing moves or state setting */
1454 if (_edit_mode == Splice) {
1455 splice_unlocked (at, distance, exclude);
1460 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1463 RegionLock rl (this);
1464 core_splice (at, distance, exclude);
1469 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1471 core_splice (at, distance, exclude);
1475 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1479 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1481 if (exclude && (*i) == exclude) {
1485 if ((*i)->position() >= at) {
1486 framepos_t new_pos = (*i)->position() + distance;
1489 } else if (new_pos >= max_framepos - (*i)->length()) {
1490 new_pos = max_framepos - (*i)->length();
1493 (*i)->set_position (new_pos);
1499 notify_length_changed ();
1503 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1505 if (in_set_state || _splicing || _nudging || _shuffling) {
1509 if (what_changed.contains (Properties::position)) {
1511 /* remove it from the list then add it back in
1512 the right place again.
1515 RegionSortByPosition cmp;
1517 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1519 if (i == regions.end()) {
1520 /* the region bounds are being modified but its not currently
1521 in the region list. we will use its bounds correctly when/if
1528 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1531 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1533 frameoffset_t delta = 0;
1535 if (what_changed.contains (Properties::position)) {
1536 delta = region->position() - region->last_position();
1539 if (what_changed.contains (Properties::length)) {
1540 delta += region->length() - region->last_length();
1544 possibly_splice (region->last_position() + region->last_length(), delta, region);
1547 if (holding_state ()) {
1548 pending_bounds.push_back (region);
1550 if (_session.config.get_layer_model() == MoveAddHigher) {
1551 /* it moved or changed length, so change the timestamp */
1552 timestamp_layer_op (region);
1555 notify_length_changed ();
1557 check_dependents (region, false);
1563 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1565 boost::shared_ptr<Region> region (weak_region.lock());
1571 /* this makes a virtual call to the right kind of playlist ... */
1573 region_changed (what_changed, region);
1577 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1579 PropertyChange our_interests;
1580 PropertyChange bounds;
1581 PropertyChange pos_and_length;
1584 if (in_set_state || in_flush) {
1588 our_interests.add (Properties::muted);
1589 our_interests.add (Properties::layer);
1590 our_interests.add (Properties::opaque);
1592 bounds.add (Properties::start);
1593 bounds.add (Properties::position);
1594 bounds.add (Properties::length);
1596 pos_and_length.add (Properties::position);
1597 pos_and_length.add (Properties::length);
1599 if (what_changed.contains (bounds)) {
1600 region_bounds_changed (what_changed, region);
1601 save = !(_splicing || _nudging);
1604 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1605 check_dependents (region, false);
1608 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1609 notify_region_moved (region);
1610 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1611 notify_region_end_trimmed (region);
1612 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1613 notify_region_start_trimmed (region);
1616 /* don't notify about layer changes, since we are the only object that can initiate
1617 them, and we notify in ::relayer()
1620 if (what_changed.contains (our_interests)) {
1628 Playlist::drop_regions ()
1630 RegionLock rl (this);
1632 all_regions.clear ();
1636 Playlist::sync_all_regions_with_regions ()
1638 RegionLock rl (this);
1640 all_regions.clear ();
1642 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1643 all_regions.insert (*i);
1648 Playlist::clear (bool with_signals)
1651 RegionLock rl (this);
1653 region_state_changed_connections.drop_connections ();
1655 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1656 pending_removes.insert (*i);
1661 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1662 remove_dependents (*s);
1668 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1669 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1672 pending_removes.clear ();
1673 pending_contents_change = false;
1679 /***********************************************************************
1681 **********************************************************************/
1683 Playlist::RegionList *
1684 Playlist::regions_at (framepos_t frame)
1687 RegionLock rlock (this);
1688 return find_regions_at (frame);
1692 Playlist::count_regions_at (framepos_t frame) const
1694 RegionLock rlock (const_cast<Playlist*>(this));
1697 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1698 if ((*i)->covers (frame)) {
1706 boost::shared_ptr<Region>
1707 Playlist::top_region_at (framepos_t frame)
1710 RegionLock rlock (this);
1711 RegionList *rlist = find_regions_at (frame);
1712 boost::shared_ptr<Region> region;
1714 if (rlist->size()) {
1715 RegionSortByLayer cmp;
1717 region = rlist->back();
1724 boost::shared_ptr<Region>
1725 Playlist::top_unmuted_region_at (framepos_t frame)
1728 RegionLock rlock (this);
1729 RegionList *rlist = find_regions_at (frame);
1731 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1733 RegionList::iterator tmp = i;
1736 if ((*i)->muted()) {
1743 boost::shared_ptr<Region> region;
1745 if (rlist->size()) {
1746 RegionSortByLayer cmp;
1748 region = rlist->back();
1755 Playlist::RegionList*
1756 Playlist::regions_to_read (framepos_t start, framepos_t end)
1758 /* Caller must hold lock */
1760 RegionList covering;
1761 set<framepos_t> to_check;
1762 set<boost::shared_ptr<Region> > unique;
1764 to_check.insert (start);
1765 to_check.insert (end);
1767 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1769 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1771 /* find all/any regions that span start+end */
1773 switch ((*i)->coverage (start, end)) {
1777 case OverlapInternal:
1778 covering.push_back (*i);
1779 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1783 to_check.insert ((*i)->position());
1784 if ((*i)->position() != 0) {
1785 to_check.insert ((*i)->position()-1);
1787 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1788 covering.push_back (*i);
1792 to_check.insert ((*i)->last_frame());
1793 to_check.insert ((*i)->last_frame()+1);
1794 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1795 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1796 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1797 covering.push_back (*i);
1800 case OverlapExternal:
1801 covering.push_back (*i);
1802 to_check.insert ((*i)->position());
1803 if ((*i)->position() != 0) {
1804 to_check.insert ((*i)->position()-1);
1806 to_check.insert ((*i)->last_frame());
1807 to_check.insert ((*i)->last_frame()+1);
1808 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1809 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1810 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1814 /* don't go too far */
1816 if ((*i)->position() > end) {
1821 RegionList* rlist = new RegionList;
1823 /* find all the regions that cover each position .... */
1825 if (covering.size() == 1) {
1827 rlist->push_back (covering.front());
1828 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1833 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1837 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1839 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1841 if ((*x)->covers (*t)) {
1842 here.push_back (*x);
1843 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1847 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1854 RegionSortByLayer cmp;
1857 /* ... and get the top/transparent regions at "here" */
1859 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1863 if ((*c)->opaque()) {
1865 /* the other regions at this position are hidden by this one */
1866 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1873 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1874 rlist->push_back (*s);
1877 if (rlist->size() > 1) {
1878 /* now sort by time order */
1880 RegionSortByPosition cmp;
1885 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1890 Playlist::RegionList *
1891 Playlist::find_regions_at (framepos_t frame)
1893 /* Caller must hold lock */
1895 RegionList *rlist = new RegionList;
1897 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1898 if ((*i)->covers (frame)) {
1899 rlist->push_back (*i);
1906 Playlist::RegionList *
1907 Playlist::regions_touched (framepos_t start, framepos_t end)
1909 RegionLock rlock (this);
1910 RegionList *rlist = new RegionList;
1912 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1913 if ((*i)->coverage (start, end) != OverlapNone) {
1914 rlist->push_back (*i);
1922 Playlist::find_next_transient (framepos_t from, int dir)
1924 RegionLock rlock (this);
1925 AnalysisFeatureList points;
1926 AnalysisFeatureList these_points;
1928 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1930 if ((*i)->last_frame() < from) {
1934 if ((*i)->first_frame() > from) {
1939 (*i)->get_transients (these_points);
1941 /* add first frame, just, err, because */
1943 these_points.push_back ((*i)->first_frame());
1945 points.insert (points.end(), these_points.begin(), these_points.end());
1946 these_points.clear ();
1949 if (points.empty()) {
1953 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1954 bool reached = false;
1957 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1962 if (reached && (*x) > from) {
1967 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1972 if (reached && (*x) < from) {
1981 boost::shared_ptr<Region>
1982 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1984 RegionLock rlock (this);
1985 boost::shared_ptr<Region> ret;
1986 framepos_t closest = max_framepos;
1988 bool end_iter = false;
1990 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1994 frameoffset_t distance;
1995 boost::shared_ptr<Region> r = (*i);
2000 pos = r->first_frame ();
2003 pos = r->last_frame ();
2006 pos = r->sync_position ();
2011 case 1: /* forwards */
2014 if ((distance = pos - frame) < closest) {
2023 default: /* backwards */
2026 if ((distance = frame - pos) < closest) {
2043 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2045 RegionLock rlock (this);
2047 framepos_t closest = max_framepos;
2048 framepos_t ret = -1;
2052 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2054 boost::shared_ptr<Region> r = (*i);
2055 frameoffset_t distance;
2057 if (r->first_frame() > frame) {
2059 distance = r->first_frame() - frame;
2061 if (distance < closest) {
2062 ret = r->first_frame();
2067 if (r->last_frame () > frame) {
2069 distance = r->last_frame () - frame;
2071 if (distance < closest) {
2072 ret = r->last_frame ();
2080 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2082 boost::shared_ptr<Region> r = (*i);
2083 frameoffset_t distance;
2085 if (r->last_frame() < frame) {
2087 distance = frame - r->last_frame();
2089 if (distance < closest) {
2090 ret = r->last_frame();
2095 if (r->first_frame() < frame) {
2097 distance = frame - r->first_frame();
2099 if (distance < closest) {
2100 ret = r->first_frame();
2111 /***********************************************************************/
2117 Playlist::mark_session_dirty ()
2119 if (!in_set_state && !holding_state ()) {
2120 _session.set_dirty();
2125 Playlist::rdiff (vector<Command*>& cmds) const
2127 RegionLock rlock (const_cast<Playlist *> (this));
2128 Stateful::rdiff (cmds);
2132 Playlist::clear_owned_changes ()
2134 RegionLock rlock (this);
2135 Stateful::clear_owned_changes ();
2139 Playlist::update (const RegionListProperty::ChangeRecord& change)
2141 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2142 name(), change.added.size(), change.removed.size()));
2145 /* add the added regions */
2146 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2147 add_region ((*i), (*i)->position());
2149 /* remove the removed regions */
2150 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2158 Playlist::set_state (const XMLNode& node, int version)
2162 XMLNodeConstIterator niter;
2163 XMLPropertyList plist;
2164 XMLPropertyConstIterator piter;
2166 boost::shared_ptr<Region> region;
2168 bool seen_region_nodes = false;
2173 if (node.name() != "Playlist") {
2180 plist = node.properties();
2184 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2188 if (prop->name() == X_("name")) {
2189 _name = prop->value();
2191 } else if (prop->name() == X_("orig-diskstream-id")) {
2192 /* XXX legacy session: fix up later */
2193 _orig_track_id = prop->value ();
2194 } else if (prop->name() == X_("orig-track-id")) {
2195 _orig_track_id = prop->value ();
2196 } else if (prop->name() == X_("frozen")) {
2197 _frozen = string_is_affirmative (prop->value());
2198 } else if (prop->name() == X_("combine-ops")) {
2199 _combine_ops = atoi (prop->value());
2205 nlist = node.children();
2207 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2211 if (child->name() == "Region") {
2213 seen_region_nodes = true;
2215 if ((prop = child->property ("id")) == 0) {
2216 error << _("region state node has no ID, ignored") << endmsg;
2220 ID id = prop->value ();
2222 if ((region = region_by_id (id))) {
2224 region->suspend_property_changes ();
2226 if (region->set_state (*child, version)) {
2227 region->resume_property_changes ();
2231 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2232 region->suspend_property_changes ();
2234 error << _("Playlist: cannot create region from XML") << endmsg;
2238 add_region (region, region->position(), 1.0);
2240 // So that layer_op ordering doesn't get screwed up
2241 region->set_last_layer_op( region->layer());
2242 region->resume_property_changes ();
2247 if (seen_region_nodes && regions.empty()) {
2251 /* update dependents, which was not done during add_region_internal
2252 due to in_set_state being true
2255 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2256 check_dependents (*r, false);
2261 notify_contents_changed ();
2264 first_set_state = false;
2269 Playlist::get_state()
2271 return state (true);
2275 Playlist::get_template()
2277 return state (false);
2280 /** @param full_state true to include regions in the returned state, otherwise false.
2283 Playlist::state (bool full_state)
2285 XMLNode *node = new XMLNode (X_("Playlist"));
2288 node->add_property (X_("id"), id().to_s());
2289 node->add_property (X_("name"), _name);
2290 node->add_property (X_("type"), _type.to_string());
2292 _orig_track_id.print (buf, sizeof (buf));
2293 node->add_property (X_("orig-track-id"), buf);
2294 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2297 RegionLock rlock (this, false);
2299 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2300 node->add_property ("combine-ops", buf);
2302 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2303 node->add_child_nocopy ((*i)->get_state());
2308 node->add_child_copy (*_extra_xml);
2315 Playlist::empty() const
2317 RegionLock rlock (const_cast<Playlist *>(this), false);
2318 return regions.empty();
2322 Playlist::n_regions() const
2324 RegionLock rlock (const_cast<Playlist *>(this), false);
2325 return regions.size();
2328 pair<framepos_t, framepos_t>
2329 Playlist::get_extent () const
2331 RegionLock rlock (const_cast<Playlist *>(this), false);
2332 return _get_extent ();
2335 pair<framepos_t, framepos_t>
2336 Playlist::_get_extent () const
2338 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2340 if (regions.empty()) {
2345 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2346 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2347 if (e.first < ext.first) {
2348 ext.first = e.first;
2350 if (e.second > ext.second) {
2351 ext.second = e.second;
2359 Playlist::bump_name (string name, Session &session)
2361 string newname = name;
2364 newname = bump_name_once (newname, '.');
2365 } while (session.playlists->by_name (newname)!=NULL);
2372 Playlist::top_layer() const
2374 RegionLock rlock (const_cast<Playlist *> (this));
2377 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2378 top = max (top, (*i)->layer());
2384 Playlist::set_edit_mode (EditMode mode)
2389 /********************
2391 ********************/
2394 Playlist::relayer ()
2396 /* never compute layers when changing state for undo/redo or setting from XML */
2398 if (in_update || in_set_state) {
2402 bool changed = false;
2404 /* Build up a new list of regions on each layer, stored in a set of lists
2405 each of which represent some period of time on some layer. The idea
2406 is to avoid having to search the entire region list to establish whether
2407 each region overlaps another */
2409 /* how many pieces to divide this playlist's time up into */
2410 int const divisions = 512;
2412 /* find the start and end positions of the regions on this playlist */
2413 framepos_t start = INT64_MAX;
2415 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2416 start = min (start, (*i)->position());
2417 end = max (end, (*i)->position() + (*i)->length());
2420 /* hence the size of each time division */
2421 double const division_size = (end - start) / double (divisions);
2423 vector<vector<RegionList> > layers;
2424 layers.push_back (vector<RegionList> (divisions));
2426 /* we want to go through regions from desired lowest to desired highest layer,
2427 which depends on the layer model
2430 RegionList copy = regions.rlist();
2432 /* sort according to the model and the layering mode that we're in */
2434 if (_explicit_relayering) {
2436 copy.sort (RegionSortByLayerWithPending ());
2438 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2440 copy.sort (RegionSortByLastLayerOp ());
2445 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2447 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2448 (*i)->set_pending_explicit_relayer (false);
2450 /* find the time divisions that this region covers; if there are no regions on the list,
2451 division_size will equal 0 and in this case we'll just say that
2452 start_division = end_division = 0.
2454 int start_division = 0;
2455 int end_division = 0;
2457 if (division_size > 0) {
2458 start_division = floor ( ((*i)->position() - start) / division_size);
2459 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2460 if (end_division == divisions) {
2465 assert (divisions == 0 || end_division < divisions);
2467 /* find the lowest layer that this region can go on */
2468 size_t j = layers.size();
2470 /* try layer j - 1; it can go on if it overlaps no other region
2471 that is already on that layer
2474 bool overlap = false;
2475 for (int k = start_division; k <= end_division; ++k) {
2476 RegionList::iterator l = layers[j-1][k].begin ();
2477 while (l != layers[j-1][k].end()) {
2478 if ((*l)->overlap_equivalent (*i)) {
2491 /* overlap, so we must use layer j */
2498 if (j == layers.size()) {
2499 /* we need a new layer for this region */
2500 layers.push_back (vector<RegionList> (divisions));
2503 /* put a reference to this region in each of the divisions that it exists in */
2504 for (int k = start_division; k <= end_division; ++k) {
2505 layers[j][k].push_back (*i);
2508 if ((*i)->layer() != j) {
2512 (*i)->set_layer (j);
2516 notify_layering_changed ();
2520 /* XXX these layer functions are all deprecated */
2523 Playlist::raise_region (boost::shared_ptr<Region> region)
2525 uint32_t top = regions.size() - 1;
2526 layer_t target = region->layer() + 1U;
2528 if (target >= top) {
2529 /* its already at the effective top */
2533 move_region_to_layer (target, region, 1);
2537 Playlist::lower_region (boost::shared_ptr<Region> region)
2539 if (region->layer() == 0) {
2540 /* its already at the bottom */
2544 layer_t target = region->layer() - 1U;
2546 move_region_to_layer (target, region, -1);
2550 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2552 /* does nothing useful if layering mode is later=higher */
2553 switch (_session.config.get_layer_model()) {
2560 layer_t top = regions.size() - 1;
2562 if (region->layer() >= top) {
2563 /* already on the top */
2567 move_region_to_layer (top, region, 1);
2568 /* mark the region's last_layer_op as now, so that it remains on top when
2569 doing future relayers (until something else takes over)
2571 timestamp_layer_op (region);
2575 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2577 /* does nothing useful if layering mode is later=higher */
2578 switch (_session.config.get_layer_model()) {
2585 if (region->layer() == 0) {
2586 /* already on the bottom */
2590 move_region_to_layer (0, region, -1);
2591 /* force region's last layer op to zero so that it stays at the bottom
2592 when doing future relayers
2594 region->set_last_layer_op (0);
2598 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2600 RegionList::iterator i;
2601 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2602 list<LayerInfo> layerinfo;
2605 RegionLock rlock (const_cast<Playlist *> (this));
2607 for (i = regions.begin(); i != regions.end(); ++i) {
2617 /* region is moving up, move all regions on intermediate layers
2621 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2622 dest = (*i)->layer() - 1;
2629 /* region is moving down, move all regions on intermediate layers
2633 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2634 dest = (*i)->layer() + 1;
2644 newpair.second = dest;
2646 layerinfo.push_back (newpair);
2652 /* now reset the layers without holding the region lock */
2654 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2655 x->first->set_layer (x->second);
2658 region->set_layer (target_layer);
2660 /* now check all dependents, since we changed the layering */
2662 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2663 check_dependents (x->first, false);
2666 check_dependents (region, false);
2667 notify_layering_changed ();
2675 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2677 RegionList::iterator i;
2683 RegionLock rlock (const_cast<Playlist *> (this));
2685 for (i = regions.begin(); i != regions.end(); ++i) {
2687 if ((*i)->position() >= start) {
2693 if ((*i)->last_frame() > max_framepos - distance) {
2694 new_pos = max_framepos - (*i)->length();
2696 new_pos = (*i)->position() + distance;
2701 if ((*i)->position() > distance) {
2702 new_pos = (*i)->position() - distance;
2708 (*i)->set_position (new_pos);
2716 notify_length_changed ();
2722 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2724 RegionLock rlock (const_cast<Playlist*> (this));
2726 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2727 if ((*r)->uses_source (src)) {
2735 boost::shared_ptr<Region>
2736 Playlist::find_region (const ID& id) const
2738 RegionLock rlock (const_cast<Playlist*> (this));
2740 /* searches all regions currently in use by the playlist */
2742 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2743 if ((*i)->id() == id) {
2748 return boost::shared_ptr<Region> ();
2752 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2754 RegionLock rlock (const_cast<Playlist*> (this));
2757 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2766 boost::shared_ptr<Region>
2767 Playlist::region_by_id (const ID& id) const
2769 /* searches all regions ever added to this playlist */
2771 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2772 if ((*i)->id() == id) {
2776 return boost::shared_ptr<Region> ();
2780 Playlist::dump () const
2782 boost::shared_ptr<Region> r;
2784 cerr << "Playlist \"" << _name << "\" " << endl
2785 << regions.size() << " regions "
2788 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2790 cerr << " " << r->name() << " ["
2791 << r->start() << "+" << r->length()
2801 Playlist::set_frozen (bool yn)
2807 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2809 region->set_last_layer_op (++layer_op_counter);
2814 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2818 if (region->locked()) {
2825 RegionLock rlock (const_cast<Playlist*> (this));
2830 RegionList::iterator next;
2832 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2833 if ((*i) == region) {
2837 if (next != regions.end()) {
2839 if ((*next)->locked()) {
2845 if ((*next)->position() != region->last_frame() + 1) {
2846 /* they didn't used to touch, so after shuffle,
2847 just have them swap positions.
2849 new_pos = (*next)->position();
2851 /* they used to touch, so after shuffle,
2852 make sure they still do. put the earlier
2853 region where the later one will end after
2856 new_pos = region->position() + (*next)->length();
2859 (*next)->set_position (region->position());
2860 region->set_position (new_pos);
2862 /* avoid a full sort */
2864 regions.erase (i); // removes the region from the list */
2866 regions.insert (next, region); // adds it back after next
2875 RegionList::iterator prev = regions.end();
2877 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2878 if ((*i) == region) {
2880 if (prev != regions.end()) {
2882 if ((*prev)->locked()) {
2887 if (region->position() != (*prev)->last_frame() + 1) {
2888 /* they didn't used to touch, so after shuffle,
2889 just have them swap positions.
2891 new_pos = region->position();
2893 /* they used to touch, so after shuffle,
2894 make sure they still do. put the earlier
2895 one where the later one will end after
2897 new_pos = (*prev)->position() + region->length();
2900 region->set_position ((*prev)->position());
2901 (*prev)->set_position (new_pos);
2903 /* avoid a full sort */
2905 regions.erase (i); // remove region
2906 regions.insert (prev, region); // insert region before prev
2922 check_dependents (region, false);
2924 notify_contents_changed();
2930 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2932 RegionLock rlock (const_cast<Playlist*> (this));
2934 if (regions.size() > 1) {
2942 Playlist::update_after_tempo_map_change ()
2944 RegionLock rlock (const_cast<Playlist*> (this));
2945 RegionList copy (regions.rlist());
2949 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2950 (*i)->update_after_tempo_map_change ();
2957 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2959 RegionLock rl (this, false);
2960 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2966 Playlist::set_explicit_relayering (bool e)
2968 if (e == false && _explicit_relayering == true) {
2970 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2971 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2972 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2973 at this point would keep regions on the same layers.
2975 From then on in, it's just you and your towel.
2978 RegionLock rl (this);
2979 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2980 (*i)->set_last_layer_op ((*i)->layer ());
2984 _explicit_relayering = e;
2989 Playlist::has_region_at (framepos_t const p) const
2991 RegionLock (const_cast<Playlist *> (this));
2993 RegionList::const_iterator i = regions.begin ();
2994 while (i != regions.end() && !(*i)->covers (p)) {
2998 return (i != regions.end());
3001 /** Remove any region that uses a given source */
3003 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
3005 RegionLock rl (this);
3007 RegionList::iterator i = regions.begin();
3008 while (i != regions.end()) {
3009 RegionList::iterator j = i;
3012 if ((*i)->uses_source (s)) {
3013 remove_region_internal (*i);
3020 /** Look from a session frame time and find the start time of the next region
3021 * which is on the top layer of this playlist.
3022 * @param t Time to look from.
3023 * @return Position of next top-layered region, or max_framepos if there isn't one.
3026 Playlist::find_next_top_layer_position (framepos_t t) const
3028 RegionLock rlock (const_cast<Playlist *> (this));
3030 layer_t const top = top_layer ();
3032 RegionList copy = regions.rlist ();
3033 copy.sort (RegionSortByPosition ());
3035 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3036 if ((*i)->position() >= t && (*i)->layer() == top) {
3037 return (*i)->position();
3041 return max_framepos;
3044 boost::shared_ptr<Region>
3045 Playlist::combine (const RegionList& r)
3048 uint32_t channels = 0;
3050 framepos_t earliest_position = max_framepos;
3051 vector<TwoRegions> old_and_new_regions;
3052 vector<boost::shared_ptr<Region> > originals;
3053 vector<boost::shared_ptr<Region> > copies;
3056 uint32_t max_level = 0;
3058 /* find the maximum depth of all the regions we're combining */
3060 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3061 max_level = max (max_level, (*i)->max_source_level());
3064 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3065 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3067 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3069 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3070 earliest_position = min (earliest_position, (*i)->position());
3073 /* enable this so that we do not try to create xfades etc. as we add
3077 pl->in_partition = true;
3079 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3081 /* copy the region */
3083 boost::shared_ptr<Region> original_region = (*i);
3084 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3086 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3087 originals.push_back (original_region);
3088 copies.push_back (copied_region);
3090 RegionFactory::add_compound_association (original_region, copied_region);
3092 /* make position relative to zero */
3094 pl->add_region (copied_region, original_region->position() - earliest_position);
3096 /* use the maximum number of channels for any region */
3098 channels = max (channels, original_region->n_channels());
3100 /* it will go above the layer of the highest existing region */
3102 layer = max (layer, original_region->layer());
3105 pl->in_partition = false;
3107 pre_combine (copies);
3109 /* add any dependent regions to the new playlist */
3111 copy_dependents (old_and_new_regions, pl.get());
3113 /* now create a new PlaylistSource for each channel in the new playlist */
3116 pair<framepos_t,framepos_t> extent = pl->get_extent();
3118 for (uint32_t chn = 0; chn < channels; ++chn) {
3119 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3123 /* now a new whole-file region using the list of sources */
3125 plist.add (Properties::start, 0);
3126 plist.add (Properties::length, extent.second);
3127 plist.add (Properties::name, parent_name);
3128 plist.add (Properties::whole_file, true);
3130 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3132 /* now the non-whole-file region that we will actually use in the
3137 plist.add (Properties::start, 0);
3138 plist.add (Properties::length, extent.second);
3139 plist.add (Properties::name, child_name);
3140 plist.add (Properties::layer, layer+1);
3142 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3144 /* remove all the selected regions from the current playlist
3149 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3153 /* do type-specific stuff with the originals and the new compound
3157 post_combine (originals, compound_region);
3159 /* add the new region at the right location */
3161 add_region (compound_region, earliest_position);
3167 return compound_region;
3171 Playlist::uncombine (boost::shared_ptr<Region> target)
3173 boost::shared_ptr<PlaylistSource> pls;
3174 boost::shared_ptr<const Playlist> pl;
3175 vector<boost::shared_ptr<Region> > originals;
3176 vector<TwoRegions> old_and_new_regions;
3178 // (1) check that its really a compound region
3180 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3184 pl = pls->playlist();
3186 framepos_t adjusted_start = 0; // gcc isn't smart enough
3187 framepos_t adjusted_end = 0; // gcc isn't smart enough
3189 /* the leftmost (earliest) edge of the compound region
3190 starts at zero in its source, or larger if it
3191 has been trimmed or content-scrolled.
3193 the rightmost (latest) edge of the compound region
3194 relative to its source is the starting point plus
3195 the length of the region.
3198 // (2) get all the original regions
3200 const RegionList& rl (pl->region_list().rlist());
3201 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3202 frameoffset_t move_offset = 0;
3204 /* there are two possibilities here:
3205 1) the playlist that the playlist source was based on
3206 is us, so just add the originals (which belonged to
3207 us anyway) back in the right place.
3209 2) the playlist that the playlist source was based on
3210 is NOT us, so we need to make copies of each of
3211 the original regions that we find, and add them
3214 bool same_playlist = (pls->original() == id());
3216 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3218 boost::shared_ptr<Region> current (*i);
3220 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3222 if (ca == cassocs.end()) {
3226 boost::shared_ptr<Region> original (ca->second);
3227 bool modified_region;
3229 if (i == rl.begin()) {
3230 move_offset = (target->position() - original->position()) - target->start();
3231 adjusted_start = original->position() + target->start();
3232 adjusted_end = adjusted_start + target->length();
3235 if (!same_playlist) {
3236 framepos_t pos = original->position();
3237 /* make a copy, but don't announce it */
3238 original = RegionFactory::create (original, false);
3239 /* the pure copy constructor resets position() to zero,
3242 original->set_position (pos);
3245 /* check to see how the original region (in the
3246 * playlist before compounding occured) overlaps
3247 * with the new state of the compound region.
3250 original->clear_changes ();
3251 modified_region = false;
3253 switch (original->coverage (adjusted_start, adjusted_end)) {
3255 /* original region does not cover any part
3256 of the current state of the compound region
3260 case OverlapInternal:
3261 /* overlap is just a small piece inside the
3262 * original so trim both ends
3264 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3265 modified_region = true;
3268 case OverlapExternal:
3269 /* overlap fully covers original, so leave it
3275 /* overlap starts within but covers end,
3276 so trim the front of the region
3278 original->trim_front (adjusted_start);
3279 modified_region = true;
3283 /* overlap covers start but ends within, so
3284 * trim the end of the region.
3286 original->trim_end (adjusted_end);
3287 modified_region = true;
3292 /* fix the position to match any movement of the compound region.
3294 original->set_position (original->position() + move_offset);
3295 modified_region = true;
3298 if (modified_region) {
3299 _session.add_command (new StatefulDiffCommand (original));
3302 /* and add to the list of regions waiting to be
3306 originals.push_back (original);
3307 old_and_new_regions.push_back (TwoRegions (*i, original));
3310 pre_uncombine (originals, target);
3312 in_partition = true;
3315 // (3) remove the compound region
3317 remove_region (target);
3319 // (4) add the constituent regions
3321 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3322 add_region ((*i), (*i)->position());
3325 /* now move dependent regions back from the compound to this playlist */
3327 pl->copy_dependents (old_and_new_regions, this);
3329 in_partition = false;
3334 Playlist::max_source_level () const
3336 RegionLock rlock (const_cast<Playlist *> (this));
3339 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3340 lvl = max (lvl, (*i)->max_source_level());
3348 Playlist::count_joined_regions () const
3350 RegionLock rlock (const_cast<Playlist *> (this));
3353 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3354 if ((*i)->max_source_level() > 0) {
3363 Playlist::set_orig_track_id (const PBD::ID& id)
3365 _orig_track_id = id;