2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <boost/lexical_cast.hpp>
31 #include "pbd/convert.h"
32 #include "pbd/failed_constructor.h"
33 #include "pbd/stateful_diff_command.h"
34 #include "pbd/xml++.h"
36 #include "ardour/debug.h"
37 #include "ardour/playlist.h"
38 #include "ardour/session.h"
39 #include "ardour/region.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/region_sorters.h"
42 #include "ardour/playlist_factory.h"
43 #include "ardour/playlist_source.h"
44 #include "ardour/transient_detector.h"
45 #include "ardour/session_playlists.h"
46 #include "ardour/source_factory.h"
51 using namespace ARDOUR;
55 namespace Properties {
56 PBD::PropertyDescriptor<bool> regions;
60 struct ShowMeTheList {
61 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
63 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
65 boost::shared_ptr<Playlist> playlist;
72 Playlist::make_property_quarks ()
74 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
75 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
76 Properties::regions.property_id));
79 RegionListProperty::RegionListProperty (Playlist& pl)
80 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
86 RegionListProperty::RegionListProperty (RegionListProperty const & p)
87 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
88 , _playlist (p._playlist)
94 RegionListProperty::clone () const
96 return new RegionListProperty (*this);
100 RegionListProperty::create () const
102 return new RegionListProperty (_playlist);
106 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
108 /* All regions (even those which are deleted) have their state saved by other
109 code, so we can just store ID here.
112 node.add_property ("id", region->id().to_s ());
115 boost::shared_ptr<Region>
116 RegionListProperty::get_content_from_xml (XMLNode const & node) const
118 XMLProperty const * prop = node.property ("id");
121 PBD::ID id (prop->value ());
123 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
126 ret = RegionFactory::region_by_id (id);
132 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
133 : SessionObject(sess, nom)
138 first_set_state = false;
143 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
144 : SessionObject(sess, "unnamed playlist")
149 const XMLProperty* prop = node.property("type");
150 assert(!prop || DataType(prop->value()) == _type);
154 _name = "unnamed"; /* reset by set_state */
157 /* set state called by derived class */
160 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
161 : SessionObject(other->_session, namestr)
163 , _type(other->_type)
164 , _orig_diskstream_id (other->_orig_diskstream_id)
169 other->copy_regions (tmp);
173 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
174 add_region_internal( (*x), (*x)->position());
179 _splicing = other->_splicing;
180 _nudging = other->_nudging;
181 _edit_mode = other->_edit_mode;
184 first_set_state = false;
186 in_partition = false;
188 _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_diskstream_id (other->_orig_diskstream_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_length = false;
308 pending_layering = false;
309 first_set_state = true;
317 _edit_mode = Config->get_edit_mode();
319 in_partition = false;
322 layer_op_counter = 0;
324 _explicit_relayering = false;
327 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
328 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
330 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
333 Playlist::~Playlist ()
335 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
338 RegionLock rl (this);
340 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
341 (*i)->set_playlist (boost::shared_ptr<Playlist>());
345 /* GoingAway must be emitted by derived classes */
349 Playlist::_set_sort_id ()
352 Playlists are given names like <track name>.<id>
353 or <track name>.<edit group name>.<id> where id
354 is an integer. We extract the id and sort by that.
357 size_t dot_position = _name.val().find_last_of(".");
359 if (dot_position == string::npos) {
362 string t = _name.val().substr(dot_position + 1);
365 _sort_id = boost::lexical_cast<int>(t);
368 catch (boost::bad_lexical_cast e) {
375 Playlist::set_name (const string& str)
377 /* in a typical situation, a playlist is being used
378 by one diskstream and also is referenced by the
379 Session. if there are more references than that,
380 then don't change the name.
387 bool ret = SessionObject::set_name(str);
394 /***********************************************************************
395 CHANGE NOTIFICATION HANDLING
397 Notifications must be delayed till the region_lock is released. This
398 is necessary because handlers for the signals may need to acquire
399 the lock (e.g. to read from the playlist).
400 ***********************************************************************/
403 Playlist::begin_undo ()
410 Playlist::end_undo ()
419 delay_notifications ();
420 g_atomic_int_inc (&ignore_state_changes);
423 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
425 Playlist::thaw (bool from_undo)
427 g_atomic_int_dec_and_test (&ignore_state_changes);
428 release_notifications (from_undo);
433 Playlist::delay_notifications ()
435 g_atomic_int_inc (&block_notifications);
436 freeze_length = _get_extent().second;
439 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
441 Playlist::release_notifications (bool from_undo)
443 if (g_atomic_int_dec_and_test (&block_notifications)) {
444 flush_notifications (from_undo);
449 Playlist::notify_contents_changed ()
451 if (holding_state ()) {
452 pending_contents_change = true;
454 pending_contents_change = false;
455 ContentsChanged(); /* EMIT SIGNAL */
460 Playlist::notify_layering_changed ()
462 if (holding_state ()) {
463 pending_layering = true;
465 pending_layering = false;
466 LayeringChanged(); /* EMIT SIGNAL */
471 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
473 if (holding_state ()) {
474 pending_removes.insert (r);
475 pending_contents_change = true;
476 pending_length = true;
478 /* this might not be true, but we have to act
479 as though it could be.
481 pending_length = false;
482 LengthChanged (); /* EMIT SIGNAL */
483 pending_contents_change = false;
484 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
485 ContentsChanged (); /* EMIT SIGNAL */
490 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
492 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
494 if (holding_state ()) {
496 pending_range_moves.push_back (move);
500 list< Evoral::RangeMove<framepos_t> > m;
502 RangesMoved (m, false);
508 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
510 if (r->position() >= r->last_position()) {
511 /* trimmed shorter */
515 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
517 if (holding_state ()) {
519 pending_region_extensions.push_back (extra);
523 list<Evoral::Range<framepos_t> > r;
531 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
533 if (r->length() < r->last_length()) {
534 /* trimmed shorter */
537 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
539 if (holding_state ()) {
541 pending_region_extensions.push_back (extra);
545 list<Evoral::Range<framepos_t> > r;
553 Playlist::notify_region_added (boost::shared_ptr<Region> r)
555 /* the length change might not be true, but we have to act
556 as though it could be.
559 if (holding_state()) {
560 pending_adds.insert (r);
561 pending_contents_change = true;
562 pending_length = true;
565 pending_length = false;
566 LengthChanged (); /* EMIT SIGNAL */
567 pending_contents_change = false;
568 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
569 ContentsChanged (); /* EMIT SIGNAL */
574 Playlist::notify_length_changed ()
576 if (holding_state ()) {
577 pending_length = true;
579 pending_length = false;
580 LengthChanged(); /* EMIT SIGNAL */
581 pending_contents_change = false;
582 ContentsChanged (); /* EMIT SIGNAL */
586 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
588 Playlist::flush_notifications (bool from_undo)
590 set<boost::shared_ptr<Region> > dependent_checks_needed;
591 set<boost::shared_ptr<Region> >::iterator s;
592 uint32_t regions_changed = false;
593 bool check_length = false;
594 framecnt_t old_length = 0;
602 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
603 regions_changed = true;
604 if (!pending_length) {
605 old_length = _get_extent ().second;
610 /* we have no idea what order the regions ended up in pending
611 bounds (it could be based on selection order, for example).
612 so, to preserve layering in the "most recently moved is higher"
613 model, sort them by existing layer, then timestamp them.
616 // RegionSortByLayer cmp;
617 // pending_bounds.sort (cmp);
619 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
620 if (_session.config.get_layer_model() == MoveAddHigher) {
621 timestamp_layer_op (*r);
623 dependent_checks_needed.insert (*r);
626 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
627 remove_dependents (*s);
628 // cerr << _name << " sends RegionRemoved\n";
629 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
632 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
633 // cerr << _name << " sends RegionAdded\n";
634 /* don't emit RegionAdded signal until relayering is done,
635 so that the region is fully setup by the time
636 anyone hear's that its been added
638 dependent_checks_needed.insert (*s);
642 if (old_length != _get_extent().second) {
643 pending_length = true;
644 // cerr << _name << " length has changed\n";
648 if (pending_length || (freeze_length != _get_extent().second)) {
649 pending_length = false;
650 // cerr << _name << " sends LengthChanged\n";
651 LengthChanged(); /* EMIT SIGNAL */
654 if (regions_changed || pending_contents_change) {
658 pending_contents_change = false;
659 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
660 ContentsChanged (); /* EMIT SIGNAL */
661 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
664 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
665 (*s)->clear_changes ();
666 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
669 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
670 check_dependents (*s, false);
673 if (!pending_range_moves.empty ()) {
674 RangesMoved (pending_range_moves, from_undo);
677 if (!pending_region_extensions.empty ()) {
678 RegionsExtended (pending_region_extensions);
687 Playlist::clear_pending ()
689 pending_adds.clear ();
690 pending_removes.clear ();
691 pending_bounds.clear ();
692 pending_range_moves.clear ();
693 pending_region_extensions.clear ();
694 pending_contents_change = false;
695 pending_length = false;
698 /*************************************************************
700 *************************************************************/
703 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
705 RegionLock rlock (this);
706 times = fabs (times);
708 int itimes = (int) floor (times);
710 framepos_t pos = position;
712 if (times == 1 && auto_partition){
713 partition(pos - 1, (pos + region->length()), true);
717 add_region_internal (region, pos);
718 pos += region->length();
723 /* note that itimes can be zero if we being asked to just
724 insert a single fraction of the region.
727 for (int i = 0; i < itimes; ++i) {
728 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
729 add_region_internal (copy, pos);
730 pos += region->length();
733 framecnt_t length = 0;
735 if (floor (times) != times) {
736 length = (framecnt_t) floor (region->length() * (times - floor (times)));
738 RegionFactory::region_name (name, region->name(), false);
743 plist.add (Properties::start, region->start());
744 plist.add (Properties::length, length);
745 plist.add (Properties::name, name);
746 plist.add (Properties::layer, region->layer());
748 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
749 add_region_internal (sub, pos);
753 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
757 Playlist::set_region_ownership ()
759 RegionLock rl (this);
760 RegionList::iterator i;
761 boost::weak_ptr<Playlist> pl (shared_from_this());
763 for (i = regions.begin(); i != regions.end(); ++i) {
764 (*i)->set_playlist (pl);
769 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
771 if (region->data_type() != _type){
775 RegionSortByPosition cmp;
777 framecnt_t old_length = 0;
779 if (!holding_state()) {
780 old_length = _get_extent().second;
783 if (!first_set_state) {
784 boost::shared_ptr<Playlist> foo (shared_from_this());
785 region->set_playlist (boost::weak_ptr<Playlist>(foo));
788 region->set_position (position);
790 timestamp_layer_op (region);
792 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
793 all_regions.insert (region);
795 possibly_splice_unlocked (position, region->length(), region);
797 if (!holding_state ()) {
798 /* layers get assigned from XML state, and are not reset during undo/redo */
802 /* we need to notify the existence of new region before checking dependents. Ick. */
804 notify_region_added (region);
807 if (!holding_state ()) {
809 check_dependents (region, false);
811 if (old_length != _get_extent().second) {
812 notify_length_changed ();
816 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
822 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
824 RegionLock rlock (this);
826 bool old_sp = _splicing;
829 remove_region_internal (old);
830 add_region_internal (newr, pos);
834 possibly_splice_unlocked (pos, old->length() - newr->length());
838 Playlist::remove_region (boost::shared_ptr<Region> region)
840 RegionLock rlock (this);
841 remove_region_internal (region);
845 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
847 RegionList::iterator i;
848 framecnt_t old_length = 0;
850 if (!holding_state()) {
851 old_length = _get_extent().second;
856 region->set_playlist (boost::weak_ptr<Playlist>());
859 /* XXX should probably freeze here .... */
861 for (i = regions.begin(); i != regions.end(); ++i) {
864 framepos_t pos = (*i)->position();
865 framecnt_t distance = (*i)->length();
869 possibly_splice_unlocked (pos, -distance);
871 if (!holding_state ()) {
873 remove_dependents (region);
875 if (old_length != _get_extent().second) {
876 notify_length_changed ();
880 notify_region_removed (region);
889 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
891 if (Config->get_use_overlap_equivalency()) {
892 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
893 if ((*i)->overlap_equivalent (other)) {
894 results.push_back ((*i));
898 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
899 if ((*i)->equivalent (other)) {
900 results.push_back ((*i));
907 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
909 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
911 if ((*i) && (*i)->region_list_equivalent (other)) {
912 results.push_back (*i);
918 Playlist::partition (framepos_t start, framepos_t end, bool cut)
922 partition_internal (start, end, cut, thawlist);
924 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
925 (*i)->resume_property_changes ();
930 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
932 RegionList new_regions;
935 RegionLock rlock (this);
937 boost::shared_ptr<Region> region;
938 boost::shared_ptr<Region> current;
940 RegionList::iterator tmp;
942 framepos_t pos1, pos2, pos3, pos4;
946 /* need to work from a copy, because otherwise the regions we add during the process
947 get operated on as well.
950 RegionList copy = regions.rlist();
952 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
959 if (current->first_frame() >= start && current->last_frame() < end) {
962 remove_region_internal (current);
968 /* coverage will return OverlapStart if the start coincides
969 with the end point. we do not partition such a region,
970 so catch this special case.
973 if (current->first_frame() >= end) {
977 if ((overlap = current->coverage (start, end)) == OverlapNone) {
981 pos1 = current->position();
984 pos4 = current->last_frame();
986 if (overlap == OverlapInternal) {
987 /* split: we need 3 new regions, the front, middle and end.
988 cut: we need 2 regions, the front and end.
993 ---------------*************************------------
996 ---------------*****++++++++++++++++====------------
998 ---------------*****----------------====------------
1003 /* "middle" ++++++ */
1005 RegionFactory::region_name (new_name, current->name(), false);
1009 plist.add (Properties::start, current->start() + (pos2 - pos1));
1010 plist.add (Properties::length, pos3 - pos2);
1011 plist.add (Properties::name, new_name);
1012 plist.add (Properties::layer, regions.size());
1013 plist.add (Properties::automatic, true);
1014 plist.add (Properties::left_of_split, true);
1015 plist.add (Properties::right_of_split, true);
1017 region = RegionFactory::create (current, plist);
1018 add_region_internal (region, start);
1019 new_regions.push_back (region);
1024 RegionFactory::region_name (new_name, current->name(), false);
1028 plist.add (Properties::start, current->start() + (pos3 - pos1));
1029 plist.add (Properties::length, pos4 - pos3);
1030 plist.add (Properties::name, new_name);
1031 plist.add (Properties::layer, regions.size());
1032 plist.add (Properties::automatic, true);
1033 plist.add (Properties::right_of_split, true);
1035 region = RegionFactory::create (current, plist);
1037 add_region_internal (region, end);
1038 new_regions.push_back (region);
1042 current->suspend_property_changes ();
1043 thawlist.push_back (current);
1044 current->cut_end (pos2 - 1);
1046 } else if (overlap == OverlapEnd) {
1050 ---------------*************************------------
1053 ---------------**************+++++++++++------------
1055 ---------------**************-----------------------
1062 RegionFactory::region_name (new_name, current->name(), false);
1066 plist.add (Properties::start, current->start() + (pos2 - pos1));
1067 plist.add (Properties::length, pos4 - pos2);
1068 plist.add (Properties::name, new_name);
1069 plist.add (Properties::layer, regions.size());
1070 plist.add (Properties::automatic, true);
1071 plist.add (Properties::left_of_split, true);
1073 region = RegionFactory::create (current, plist);
1075 add_region_internal (region, start);
1076 new_regions.push_back (region);
1081 current->suspend_property_changes ();
1082 thawlist.push_back (current);
1083 current->cut_end (pos2 - 1);
1085 } else if (overlap == OverlapStart) {
1087 /* split: we need 2 regions: the front and the end.
1088 cut: just trim current to skip the cut area
1093 ---------------*************************------------
1097 ---------------****+++++++++++++++++++++------------
1099 -------------------*********************------------
1105 RegionFactory::region_name (new_name, current->name(), false);
1109 plist.add (Properties::start, current->start());
1110 plist.add (Properties::length, pos3 - pos1);
1111 plist.add (Properties::name, new_name);
1112 plist.add (Properties::layer, regions.size());
1113 plist.add (Properties::automatic, true);
1114 plist.add (Properties::right_of_split, true);
1116 region = RegionFactory::create (current, plist);
1118 add_region_internal (region, pos1);
1119 new_regions.push_back (region);
1124 current->suspend_property_changes ();
1125 thawlist.push_back (current);
1126 current->trim_front (pos3);
1127 } else if (overlap == OverlapExternal) {
1129 /* split: no split required.
1130 cut: remove the region.
1135 ---------------*************************------------
1139 ---------------*************************------------
1141 ----------------------------------------------------
1146 remove_region_internal (current);
1149 new_regions.push_back (current);
1153 in_partition = false;
1156 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1157 check_dependents (*i, false);
1161 boost::shared_ptr<Playlist>
1162 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1164 boost::shared_ptr<Playlist> ret;
1165 boost::shared_ptr<Playlist> pl;
1168 if (ranges.empty()) {
1169 return boost::shared_ptr<Playlist>();
1172 start = ranges.front().start;
1174 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1176 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1178 if (i == ranges.begin()) {
1182 /* paste the next section into the nascent playlist,
1183 offset to reflect the start of the first range we
1187 ret->paste (pl, (*i).start - start, 1.0f);
1194 boost::shared_ptr<Playlist>
1195 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1197 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1198 return cut_copy (pmf, ranges, result_is_hidden);
1201 boost::shared_ptr<Playlist>
1202 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1204 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1205 return cut_copy (pmf, ranges, result_is_hidden);
1208 boost::shared_ptr<Playlist>
1209 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1211 boost::shared_ptr<Playlist> the_copy;
1212 RegionList thawlist;
1215 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1216 string new_name = _name;
1220 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1221 return boost::shared_ptr<Playlist>();
1224 partition_internal (start, start+cnt-1, true, thawlist);
1226 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1227 (*i)->resume_property_changes();
1233 boost::shared_ptr<Playlist>
1234 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1238 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1239 string new_name = _name;
1243 cnt = min (_get_extent().second - start, cnt);
1244 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1248 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1250 times = fabs (times);
1253 RegionLock rl1 (this);
1254 RegionLock rl2 (other.get());
1256 framecnt_t const old_length = _get_extent().second;
1258 int itimes = (int) floor (times);
1259 framepos_t pos = position;
1260 framecnt_t const shift = other->_get_extent().second;
1261 layer_t top_layer = regions.size();
1264 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1265 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1267 /* put these new regions on top of all existing ones, but preserve
1268 the ordering they had in the original playlist.
1271 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1272 add_region_internal (copy_of_region, (*i)->position() + pos);
1278 /* XXX shall we handle fractional cases at some point? */
1280 if (old_length != _get_extent().second) {
1281 notify_length_changed ();
1292 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1294 times = fabs (times);
1296 RegionLock rl (this);
1297 int itimes = (int) floor (times);
1298 framepos_t pos = position + 1;
1301 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1302 add_region_internal (copy, pos);
1303 pos += region->length();
1306 if (floor (times) != times) {
1307 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1309 RegionFactory::region_name (name, region->name(), false);
1314 plist.add (Properties::start, region->start());
1315 plist.add (Properties::length, length);
1316 plist.add (Properties::name, name);
1318 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1319 add_region_internal (sub, pos);
1325 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1327 RegionLock rlock (this);
1328 RegionList copy (regions.rlist());
1331 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1333 if ((*r)->last_frame() < at) {
1338 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1339 /* intersected region */
1340 if (!move_intersected) {
1345 /* do not move regions glued to music time - that
1346 has to be done separately.
1349 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1350 fixup.push_back (*r);
1354 (*r)->set_position ((*r)->position() + distance);
1357 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1358 (*r)->recompute_position_from_lock_style ();
1363 Playlist::split (framepos_t at)
1365 RegionLock rlock (this);
1366 RegionList copy (regions.rlist());
1368 /* use a copy since this operation can modify the region list
1371 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1372 _split_region (*r, at);
1377 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1379 RegionLock rl (this);
1380 _split_region (region, playlist_position);
1384 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1386 if (!region->covers (playlist_position)) {
1390 if (region->position() == playlist_position ||
1391 region->last_frame() == playlist_position) {
1395 boost::shared_ptr<Region> left;
1396 boost::shared_ptr<Region> right;
1397 frameoffset_t before;
1398 frameoffset_t after;
1402 /* split doesn't change anything about length, so don't try to splice */
1404 bool old_sp = _splicing;
1407 before = playlist_position - region->position();
1408 after = region->length() - before;
1410 RegionFactory::region_name (before_name, region->name(), false);
1415 plist.add (Properties::position, region->position ());
1416 plist.add (Properties::length, before);
1417 plist.add (Properties::name, before_name);
1418 plist.add (Properties::left_of_split, true);
1420 /* note: we must use the version of ::create with an offset here,
1421 since it supplies that offset to the Region constructor, which
1422 is necessary to get audio region gain envelopes right.
1424 left = RegionFactory::create (region, 0, plist);
1427 RegionFactory::region_name (after_name, region->name(), false);
1432 plist.add (Properties::position, region->position() + before);
1433 plist.add (Properties::length, after);
1434 plist.add (Properties::name, after_name);
1435 plist.add (Properties::right_of_split, true);
1437 /* same note as above */
1438 right = RegionFactory::create (region, before, plist);
1441 add_region_internal (left, region->position());
1442 add_region_internal (right, region->position() + before);
1444 uint64_t orig_layer_op = region->last_layer_op();
1445 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1446 if ((*i)->last_layer_op() > orig_layer_op) {
1447 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1451 left->set_last_layer_op ( orig_layer_op );
1452 right->set_last_layer_op ( orig_layer_op + 1);
1456 finalize_split_region (region, left, right);
1458 remove_region_internal (region);
1464 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1466 if (_splicing || in_set_state) {
1467 /* don't respond to splicing moves or state setting */
1471 if (_edit_mode == Splice) {
1472 splice_locked (at, distance, exclude);
1477 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1479 if (_splicing || in_set_state) {
1480 /* don't respond to splicing moves or state setting */
1484 if (_edit_mode == Splice) {
1485 splice_unlocked (at, distance, exclude);
1490 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1493 RegionLock rl (this);
1494 core_splice (at, distance, exclude);
1499 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1501 core_splice (at, distance, exclude);
1505 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1509 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1511 if (exclude && (*i) == exclude) {
1515 if ((*i)->position() >= at) {
1516 framepos_t new_pos = (*i)->position() + distance;
1519 } else if (new_pos >= max_framepos - (*i)->length()) {
1520 new_pos = max_framepos - (*i)->length();
1523 (*i)->set_position (new_pos);
1529 notify_length_changed ();
1533 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1535 if (in_set_state || _splicing || _nudging || _shuffling) {
1539 if (what_changed.contains (Properties::position)) {
1541 /* remove it from the list then add it back in
1542 the right place again.
1545 RegionSortByPosition cmp;
1547 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1549 if (i == regions.end()) {
1550 /* the region bounds are being modified but its not currently
1551 in the region list. we will use its bounds correctly when/if
1558 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1561 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1563 frameoffset_t delta = 0;
1565 if (what_changed.contains (Properties::position)) {
1566 delta = region->position() - region->last_position();
1569 if (what_changed.contains (Properties::length)) {
1570 delta += region->length() - region->last_length();
1574 possibly_splice (region->last_position() + region->last_length(), delta, region);
1577 if (holding_state ()) {
1578 pending_bounds.push_back (region);
1580 if (_session.config.get_layer_model() == MoveAddHigher) {
1581 /* it moved or changed length, so change the timestamp */
1582 timestamp_layer_op (region);
1585 notify_length_changed ();
1587 check_dependents (region, false);
1593 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1595 boost::shared_ptr<Region> region (weak_region.lock());
1601 /* this makes a virtual call to the right kind of playlist ... */
1603 region_changed (what_changed, region);
1607 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1609 PropertyChange our_interests;
1610 PropertyChange bounds;
1611 PropertyChange pos_and_length;
1614 if (in_set_state || in_flush) {
1618 our_interests.add (Properties::muted);
1619 our_interests.add (Properties::layer);
1620 our_interests.add (Properties::opaque);
1622 bounds.add (Properties::start);
1623 bounds.add (Properties::position);
1624 bounds.add (Properties::length);
1626 pos_and_length.add (Properties::position);
1627 pos_and_length.add (Properties::length);
1629 if (what_changed.contains (bounds)) {
1630 region_bounds_changed (what_changed, region);
1631 save = !(_splicing || _nudging);
1634 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1635 check_dependents (region, false);
1638 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1639 notify_region_moved (region);
1640 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1641 notify_region_end_trimmed (region);
1642 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1643 notify_region_start_trimmed (region);
1646 /* don't notify about layer changes, since we are the only object that can initiate
1647 them, and we notify in ::relayer()
1650 if (what_changed.contains (our_interests)) {
1658 Playlist::drop_regions ()
1660 RegionLock rl (this);
1662 all_regions.clear ();
1666 Playlist::sync_all_regions_with_regions ()
1668 RegionLock rl (this);
1670 all_regions.clear ();
1672 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1673 all_regions.insert (*i);
1678 Playlist::clear (bool with_signals)
1681 RegionLock rl (this);
1683 region_state_changed_connections.drop_connections ();
1685 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1686 pending_removes.insert (*i);
1691 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1692 remove_dependents (*s);
1698 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1699 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1702 pending_removes.clear ();
1703 pending_length = false;
1705 pending_contents_change = false;
1711 /***********************************************************************
1713 **********************************************************************/
1715 Playlist::RegionList *
1716 Playlist::regions_at (framepos_t frame)
1719 RegionLock rlock (this);
1720 return find_regions_at (frame);
1724 Playlist::count_regions_at (framepos_t frame) const
1726 RegionLock rlock (const_cast<Playlist*>(this));
1729 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1730 if ((*i)->covers (frame)) {
1738 boost::shared_ptr<Region>
1739 Playlist::top_region_at (framepos_t frame)
1742 RegionLock rlock (this);
1743 RegionList *rlist = find_regions_at (frame);
1744 boost::shared_ptr<Region> region;
1746 if (rlist->size()) {
1747 RegionSortByLayer cmp;
1749 region = rlist->back();
1756 boost::shared_ptr<Region>
1757 Playlist::top_unmuted_region_at (framepos_t frame)
1760 RegionLock rlock (this);
1761 RegionList *rlist = find_regions_at (frame);
1763 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1765 RegionList::iterator tmp = i;
1768 if ((*i)->muted()) {
1775 boost::shared_ptr<Region> region;
1777 if (rlist->size()) {
1778 RegionSortByLayer cmp;
1780 region = rlist->back();
1787 Playlist::RegionList*
1788 Playlist::regions_to_read (framepos_t start, framepos_t end)
1790 /* Caller must hold lock */
1792 RegionList covering;
1793 set<framepos_t> to_check;
1794 set<boost::shared_ptr<Region> > unique;
1796 to_check.insert (start);
1797 to_check.insert (end);
1799 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1801 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1803 /* find all/any regions that span start+end */
1805 switch ((*i)->coverage (start, end)) {
1809 case OverlapInternal:
1810 covering.push_back (*i);
1811 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1815 to_check.insert ((*i)->position());
1816 if ((*i)->position() != 0) {
1817 to_check.insert ((*i)->position()-1);
1819 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1820 covering.push_back (*i);
1824 to_check.insert ((*i)->last_frame());
1825 to_check.insert ((*i)->last_frame()+1);
1826 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1827 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1828 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1829 covering.push_back (*i);
1832 case OverlapExternal:
1833 covering.push_back (*i);
1834 to_check.insert ((*i)->position());
1835 if ((*i)->position() != 0) {
1836 to_check.insert ((*i)->position()-1);
1838 to_check.insert ((*i)->last_frame());
1839 to_check.insert ((*i)->last_frame()+1);
1840 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1841 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1842 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1846 /* don't go too far */
1848 if ((*i)->position() > end) {
1853 RegionList* rlist = new RegionList;
1855 /* find all the regions that cover each position .... */
1857 if (covering.size() == 1) {
1859 rlist->push_back (covering.front());
1860 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1865 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1869 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1871 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1873 if ((*x)->covers (*t)) {
1874 here.push_back (*x);
1875 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1879 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1886 RegionSortByLayer cmp;
1889 /* ... and get the top/transparent regions at "here" */
1891 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1895 if ((*c)->opaque()) {
1897 /* the other regions at this position are hidden by this one */
1898 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1905 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1906 rlist->push_back (*s);
1909 if (rlist->size() > 1) {
1910 /* now sort by time order */
1912 RegionSortByPosition cmp;
1917 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1922 Playlist::RegionList *
1923 Playlist::find_regions_at (framepos_t frame)
1925 /* Caller must hold lock */
1927 RegionList *rlist = new RegionList;
1929 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1930 if ((*i)->covers (frame)) {
1931 rlist->push_back (*i);
1938 Playlist::RegionList *
1939 Playlist::regions_touched (framepos_t start, framepos_t end)
1941 RegionLock rlock (this);
1942 RegionList *rlist = new RegionList;
1944 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1945 if ((*i)->coverage (start, end) != OverlapNone) {
1946 rlist->push_back (*i);
1954 Playlist::find_next_transient (framepos_t from, int dir)
1956 RegionLock rlock (this);
1957 AnalysisFeatureList points;
1958 AnalysisFeatureList these_points;
1960 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1962 if ((*i)->last_frame() < from) {
1966 if ((*i)->first_frame() > from) {
1971 (*i)->get_transients (these_points);
1973 /* add first frame, just, err, because */
1975 these_points.push_back ((*i)->first_frame());
1977 points.insert (points.end(), these_points.begin(), these_points.end());
1978 these_points.clear ();
1981 if (points.empty()) {
1985 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1986 bool reached = false;
1989 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1994 if (reached && (*x) > from) {
1999 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2004 if (reached && (*x) < from) {
2013 boost::shared_ptr<Region>
2014 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2016 RegionLock rlock (this);
2017 boost::shared_ptr<Region> ret;
2018 framepos_t closest = max_framepos;
2020 bool end_iter = false;
2022 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2026 frameoffset_t distance;
2027 boost::shared_ptr<Region> r = (*i);
2032 pos = r->first_frame ();
2035 pos = r->last_frame ();
2038 pos = r->sync_position ();
2043 case 1: /* forwards */
2046 if ((distance = pos - frame) < closest) {
2055 default: /* backwards */
2058 if ((distance = frame - pos) < closest) {
2075 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2077 RegionLock rlock (this);
2079 framepos_t closest = max_framepos;
2080 framepos_t ret = -1;
2084 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2086 boost::shared_ptr<Region> r = (*i);
2087 frameoffset_t distance;
2089 if (r->first_frame() > frame) {
2091 distance = r->first_frame() - frame;
2093 if (distance < closest) {
2094 ret = r->first_frame();
2099 if (r->last_frame () > frame) {
2101 distance = r->last_frame () - frame;
2103 if (distance < closest) {
2104 ret = r->last_frame ();
2112 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2114 boost::shared_ptr<Region> r = (*i);
2115 frameoffset_t distance;
2117 if (r->last_frame() < frame) {
2119 distance = frame - r->last_frame();
2121 if (distance < closest) {
2122 ret = r->last_frame();
2127 if (r->first_frame() < frame) {
2129 distance = frame - r->first_frame();
2131 if (distance < closest) {
2132 ret = r->first_frame();
2143 /***********************************************************************/
2149 Playlist::mark_session_dirty ()
2151 if (!in_set_state && !holding_state ()) {
2152 _session.set_dirty();
2157 Playlist::rdiff (vector<Command*>& cmds) const
2159 RegionLock rlock (const_cast<Playlist *> (this));
2160 Stateful::rdiff (cmds);
2164 Playlist::clear_owned_changes ()
2166 RegionLock rlock (this);
2167 Stateful::clear_owned_changes ();
2171 Playlist::update (const RegionListProperty::ChangeRecord& change)
2173 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2174 name(), change.added.size(), change.removed.size()));
2177 /* add the added regions */
2178 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2179 add_region ((*i), (*i)->position());
2181 /* remove the removed regions */
2182 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2190 Playlist::set_state (const XMLNode& node, int version)
2194 XMLNodeConstIterator niter;
2195 XMLPropertyList plist;
2196 XMLPropertyConstIterator piter;
2198 boost::shared_ptr<Region> region;
2200 bool seen_region_nodes = false;
2205 if (node.name() != "Playlist") {
2212 plist = node.properties();
2216 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2220 if (prop->name() == X_("name")) {
2221 _name = prop->value();
2223 } else if (prop->name() == X_("orig-diskstream-id")) {
2224 _orig_diskstream_id = prop->value ();
2225 } else if (prop->name() == X_("frozen")) {
2226 _frozen = string_is_affirmative (prop->value());
2227 } else if (prop->name() == X_("combine-ops")) {
2228 _combine_ops = atoi (prop->value());
2234 nlist = node.children();
2236 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2240 if (child->name() == "Region") {
2242 seen_region_nodes = true;
2244 if ((prop = child->property ("id")) == 0) {
2245 error << _("region state node has no ID, ignored") << endmsg;
2249 ID id = prop->value ();
2251 if ((region = region_by_id (id))) {
2253 region->suspend_property_changes ();
2255 if (region->set_state (*child, version)) {
2256 region->resume_property_changes ();
2260 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2261 region->suspend_property_changes ();
2263 error << _("Playlist: cannot create region from XML") << endmsg;
2267 add_region (region, region->position(), 1.0);
2269 // So that layer_op ordering doesn't get screwed up
2270 region->set_last_layer_op( region->layer());
2271 region->resume_property_changes ();
2276 if (seen_region_nodes && regions.empty()) {
2280 /* update dependents, which was not done during add_region_internal
2281 due to in_set_state being true
2284 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2285 check_dependents (*r, false);
2290 notify_contents_changed ();
2293 first_set_state = false;
2298 Playlist::get_state()
2300 return state (true);
2304 Playlist::get_template()
2306 return state (false);
2309 /** @param full_state true to include regions in the returned state, otherwise false.
2312 Playlist::state (bool full_state)
2314 XMLNode *node = new XMLNode (X_("Playlist"));
2317 node->add_property (X_("id"), id().to_s());
2318 node->add_property (X_("name"), _name);
2319 node->add_property (X_("type"), _type.to_string());
2321 _orig_diskstream_id.print (buf, sizeof (buf));
2322 node->add_property (X_("orig-diskstream-id"), buf);
2323 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2326 RegionLock rlock (this, false);
2328 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2329 node->add_property ("combine-ops", buf);
2331 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2332 node->add_child_nocopy ((*i)->get_state());
2337 node->add_child_copy (*_extra_xml);
2344 Playlist::empty() const
2346 RegionLock rlock (const_cast<Playlist *>(this), false);
2347 return regions.empty();
2351 Playlist::n_regions() const
2353 RegionLock rlock (const_cast<Playlist *>(this), false);
2354 return regions.size();
2357 pair<framepos_t, framepos_t>
2358 Playlist::get_extent () const
2360 RegionLock rlock (const_cast<Playlist *>(this), false);
2361 return _get_extent ();
2364 pair<framepos_t, framepos_t>
2365 Playlist::_get_extent () const
2367 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2369 if (regions.empty()) {
2374 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2375 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2376 if (e.first < ext.first) {
2377 ext.first = e.first;
2379 if (e.second > ext.second) {
2380 ext.second = e.second;
2388 Playlist::bump_name (string name, Session &session)
2390 string newname = name;
2393 newname = bump_name_once (newname, '.');
2394 } while (session.playlists->by_name (newname)!=NULL);
2401 Playlist::top_layer() const
2403 RegionLock rlock (const_cast<Playlist *> (this));
2406 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2407 top = max (top, (*i)->layer());
2413 Playlist::set_edit_mode (EditMode mode)
2418 /********************
2420 ********************/
2423 Playlist::relayer ()
2425 /* never compute layers when changing state for undo/redo or setting from XML */
2427 if (in_update || in_set_state) {
2431 bool changed = false;
2433 /* Build up a new list of regions on each layer, stored in a set of lists
2434 each of which represent some period of time on some layer. The idea
2435 is to avoid having to search the entire region list to establish whether
2436 each region overlaps another */
2438 /* how many pieces to divide this playlist's time up into */
2439 int const divisions = 512;
2441 /* find the start and end positions of the regions on this playlist */
2442 framepos_t start = INT64_MAX;
2444 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2445 start = min (start, (*i)->position());
2446 end = max (end, (*i)->position() + (*i)->length());
2449 /* hence the size of each time division */
2450 double const division_size = (end - start) / double (divisions);
2452 vector<vector<RegionList> > layers;
2453 layers.push_back (vector<RegionList> (divisions));
2455 /* we want to go through regions from desired lowest to desired highest layer,
2456 which depends on the layer model
2459 RegionList copy = regions.rlist();
2461 /* sort according to the model and the layering mode that we're in */
2463 if (_explicit_relayering) {
2465 copy.sort (RegionSortByLayerWithPending ());
2467 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2469 copy.sort (RegionSortByLastLayerOp ());
2474 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2476 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2477 (*i)->set_pending_explicit_relayer (false);
2479 /* find the time divisions that this region covers; if there are no regions on the list,
2480 division_size will equal 0 and in this case we'll just say that
2481 start_division = end_division = 0.
2483 int start_division = 0;
2484 int end_division = 0;
2486 if (division_size > 0) {
2487 start_division = floor ( ((*i)->position() - start) / division_size);
2488 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2489 if (end_division == divisions) {
2494 assert (divisions == 0 || end_division < divisions);
2496 /* find the lowest layer that this region can go on */
2497 size_t j = layers.size();
2499 /* try layer j - 1; it can go on if it overlaps no other region
2500 that is already on that layer
2503 bool overlap = false;
2504 for (int k = start_division; k <= end_division; ++k) {
2505 RegionList::iterator l = layers[j-1][k].begin ();
2506 while (l != layers[j-1][k].end()) {
2507 if ((*l)->overlap_equivalent (*i)) {
2520 /* overlap, so we must use layer j */
2527 if (j == layers.size()) {
2528 /* we need a new layer for this region */
2529 layers.push_back (vector<RegionList> (divisions));
2532 /* put a reference to this region in each of the divisions that it exists in */
2533 for (int k = start_division; k <= end_division; ++k) {
2534 layers[j][k].push_back (*i);
2537 if ((*i)->layer() != j) {
2541 (*i)->set_layer (j);
2545 notify_layering_changed ();
2549 /* XXX these layer functions are all deprecated */
2552 Playlist::raise_region (boost::shared_ptr<Region> region)
2554 uint32_t top = regions.size() - 1;
2555 layer_t target = region->layer() + 1U;
2557 if (target >= top) {
2558 /* its already at the effective top */
2562 move_region_to_layer (target, region, 1);
2566 Playlist::lower_region (boost::shared_ptr<Region> region)
2568 if (region->layer() == 0) {
2569 /* its already at the bottom */
2573 layer_t target = region->layer() - 1U;
2575 move_region_to_layer (target, region, -1);
2579 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2581 /* does nothing useful if layering mode is later=higher */
2582 switch (_session.config.get_layer_model()) {
2589 layer_t top = regions.size() - 1;
2591 if (region->layer() >= top) {
2592 /* already on the top */
2596 move_region_to_layer (top, region, 1);
2597 /* mark the region's last_layer_op as now, so that it remains on top when
2598 doing future relayers (until something else takes over)
2600 timestamp_layer_op (region);
2604 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2606 /* does nothing useful if layering mode is later=higher */
2607 switch (_session.config.get_layer_model()) {
2614 if (region->layer() == 0) {
2615 /* already on the bottom */
2619 move_region_to_layer (0, region, -1);
2620 /* force region's last layer op to zero so that it stays at the bottom
2621 when doing future relayers
2623 region->set_last_layer_op (0);
2627 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2629 RegionList::iterator i;
2630 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2631 list<LayerInfo> layerinfo;
2634 RegionLock rlock (const_cast<Playlist *> (this));
2636 for (i = regions.begin(); i != regions.end(); ++i) {
2646 /* region is moving up, move all regions on intermediate layers
2650 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2651 dest = (*i)->layer() - 1;
2658 /* region is moving down, move all regions on intermediate layers
2662 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2663 dest = (*i)->layer() + 1;
2673 newpair.second = dest;
2675 layerinfo.push_back (newpair);
2681 /* now reset the layers without holding the region lock */
2683 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2684 x->first->set_layer (x->second);
2687 region->set_layer (target_layer);
2689 /* now check all dependents, since we changed the layering */
2691 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2692 check_dependents (x->first, false);
2695 check_dependents (region, false);
2696 notify_layering_changed ();
2704 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2706 RegionList::iterator i;
2712 RegionLock rlock (const_cast<Playlist *> (this));
2714 for (i = regions.begin(); i != regions.end(); ++i) {
2716 if ((*i)->position() >= start) {
2722 if ((*i)->last_frame() > max_framepos - distance) {
2723 new_pos = max_framepos - (*i)->length();
2725 new_pos = (*i)->position() + distance;
2730 if ((*i)->position() > distance) {
2731 new_pos = (*i)->position() - distance;
2737 (*i)->set_position (new_pos);
2745 notify_length_changed ();
2751 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2753 RegionLock rlock (const_cast<Playlist*> (this));
2755 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2756 if ((*r)->uses_source (src)) {
2764 boost::shared_ptr<Region>
2765 Playlist::find_region (const ID& id) const
2767 RegionLock rlock (const_cast<Playlist*> (this));
2769 /* searches all regions currently in use by the playlist */
2771 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2772 if ((*i)->id() == id) {
2777 return boost::shared_ptr<Region> ();
2781 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2783 RegionLock rlock (const_cast<Playlist*> (this));
2786 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2795 boost::shared_ptr<Region>
2796 Playlist::region_by_id (const ID& id) const
2798 /* searches all regions ever added to this playlist */
2800 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2801 if ((*i)->id() == id) {
2805 return boost::shared_ptr<Region> ();
2809 Playlist::dump () const
2811 boost::shared_ptr<Region> r;
2813 cerr << "Playlist \"" << _name << "\" " << endl
2814 << regions.size() << " regions "
2817 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2819 cerr << " " << r->name() << " ["
2820 << r->start() << "+" << r->length()
2830 Playlist::set_frozen (bool yn)
2836 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2838 region->set_last_layer_op (++layer_op_counter);
2843 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2847 if (region->locked()) {
2854 RegionLock rlock (const_cast<Playlist*> (this));
2859 RegionList::iterator next;
2861 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2862 if ((*i) == region) {
2866 if (next != regions.end()) {
2868 if ((*next)->locked()) {
2874 if ((*next)->position() != region->last_frame() + 1) {
2875 /* they didn't used to touch, so after shuffle,
2876 just have them swap positions.
2878 new_pos = (*next)->position();
2880 /* they used to touch, so after shuffle,
2881 make sure they still do. put the earlier
2882 region where the later one will end after
2885 new_pos = region->position() + (*next)->length();
2888 (*next)->set_position (region->position());
2889 region->set_position (new_pos);
2891 /* avoid a full sort */
2893 regions.erase (i); // removes the region from the list */
2895 regions.insert (next, region); // adds it back after next
2904 RegionList::iterator prev = regions.end();
2906 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2907 if ((*i) == region) {
2909 if (prev != regions.end()) {
2911 if ((*prev)->locked()) {
2916 if (region->position() != (*prev)->last_frame() + 1) {
2917 /* they didn't used to touch, so after shuffle,
2918 just have them swap positions.
2920 new_pos = region->position();
2922 /* they used to touch, so after shuffle,
2923 make sure they still do. put the earlier
2924 one where the later one will end after
2926 new_pos = (*prev)->position() + region->length();
2929 region->set_position ((*prev)->position());
2930 (*prev)->set_position (new_pos);
2932 /* avoid a full sort */
2934 regions.erase (i); // remove region
2935 regions.insert (prev, region); // insert region before prev
2951 check_dependents (region, false);
2953 notify_contents_changed();
2959 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2961 RegionLock rlock (const_cast<Playlist*> (this));
2963 if (regions.size() > 1) {
2971 Playlist::update_after_tempo_map_change ()
2973 RegionLock rlock (const_cast<Playlist*> (this));
2974 RegionList copy (regions.rlist());
2978 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2979 (*i)->update_position_after_tempo_map_change ();
2986 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2988 RegionLock rl (this, false);
2989 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2995 Playlist::set_explicit_relayering (bool e)
2997 if (e == false && _explicit_relayering == true) {
2999 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
3000 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
3001 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
3002 at this point would keep regions on the same layers.
3004 From then on in, it's just you and your towel.
3007 RegionLock rl (this);
3008 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
3009 (*i)->set_last_layer_op ((*i)->layer ());
3013 _explicit_relayering = e;
3018 Playlist::has_region_at (framepos_t const p) const
3020 RegionLock (const_cast<Playlist *> (this));
3022 RegionList::const_iterator i = regions.begin ();
3023 while (i != regions.end() && !(*i)->covers (p)) {
3027 return (i != regions.end());
3030 /** Remove any region that uses a given source */
3032 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
3034 RegionLock rl (this);
3036 RegionList::iterator i = regions.begin();
3037 while (i != regions.end()) {
3038 RegionList::iterator j = i;
3041 if ((*i)->uses_source (s)) {
3042 remove_region_internal (*i);
3049 /** Look from a session frame time and find the start time of the next region
3050 * which is on the top layer of this playlist.
3051 * @param t Time to look from.
3052 * @return Position of next top-layered region, or max_framepos if there isn't one.
3055 Playlist::find_next_top_layer_position (framepos_t t) const
3057 RegionLock rlock (const_cast<Playlist *> (this));
3059 layer_t const top = top_layer ();
3061 RegionList copy = regions.rlist ();
3062 copy.sort (RegionSortByPosition ());
3064 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3065 if ((*i)->position() >= t && (*i)->layer() == top) {
3066 return (*i)->position();
3070 return max_framepos;
3073 boost::shared_ptr<Region>
3074 Playlist::combine (const RegionList& r)
3077 uint32_t channels = 0;
3079 framepos_t earliest_position = max_framepos;
3080 vector<TwoRegions> old_and_new_regions;
3081 vector<boost::shared_ptr<Region> > originals;
3082 vector<boost::shared_ptr<Region> > copies;
3085 uint32_t max_level = 0;
3087 /* find the maximum depth of all the regions we're combining */
3089 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3090 max_level = max (max_level, (*i)->max_source_level());
3093 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3094 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3096 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3098 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3099 earliest_position = min (earliest_position, (*i)->position());
3102 /* enable this so that we do not try to create xfades etc. as we add
3106 pl->in_partition = true;
3108 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3110 /* copy the region */
3112 boost::shared_ptr<Region> original_region = (*i);
3113 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3115 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3116 originals.push_back (original_region);
3117 copies.push_back (copied_region);
3119 RegionFactory::add_compound_association (original_region, copied_region);
3121 /* make position relative to zero */
3123 pl->add_region (copied_region, original_region->position() - earliest_position);
3125 /* use the maximum number of channels for any region */
3127 channels = max (channels, original_region->n_channels());
3129 /* it will go above the layer of the highest existing region */
3131 layer = max (layer, original_region->layer());
3134 pl->in_partition = false;
3136 pre_combine (copies);
3138 /* add any dependent regions to the new playlist */
3140 copy_dependents (old_and_new_regions, pl.get());
3142 /* now create a new PlaylistSource for each channel in the new playlist */
3145 pair<framepos_t,framepos_t> extent = pl->get_extent();
3147 for (uint32_t chn = 0; chn < channels; ++chn) {
3148 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3152 /* now a new whole-file region using the list of sources */
3154 plist.add (Properties::start, 0);
3155 plist.add (Properties::length, extent.second);
3156 plist.add (Properties::name, parent_name);
3157 plist.add (Properties::whole_file, true);
3159 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3161 /* now the non-whole-file region that we will actually use in the
3166 plist.add (Properties::start, 0);
3167 plist.add (Properties::length, extent.second);
3168 plist.add (Properties::name, child_name);
3169 plist.add (Properties::layer, layer+1);
3171 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3173 /* remove all the selected regions from the current playlist
3178 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3182 /* do type-specific stuff with the originals and the new compound
3186 post_combine (originals, compound_region);
3188 /* add the new region at the right location */
3190 add_region (compound_region, earliest_position);
3196 return compound_region;
3200 Playlist::uncombine (boost::shared_ptr<Region> target)
3202 boost::shared_ptr<PlaylistSource> pls;
3203 boost::shared_ptr<const Playlist> pl;
3204 vector<boost::shared_ptr<Region> > originals;
3205 vector<TwoRegions> old_and_new_regions;
3207 // (1) check that its really a compound region
3209 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3213 pl = pls->playlist();
3215 framepos_t adjusted_start = 0; // gcc isn't smart enough
3216 framepos_t adjusted_end = 0; // gcc isn't smart enough
3218 /* the leftmost (earliest) edge of the compound region
3219 starts at zero in its source, or larger if it
3220 has been trimmed or content-scrolled.
3222 the rightmost (latest) edge of the compound region
3223 relative to its source is the starting point plus
3224 the length of the region.
3227 // (2) get all the original regions
3229 const RegionList& rl (pl->region_list().rlist());
3230 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3231 frameoffset_t move_offset = 0;
3233 /* there are two possibilities here:
3234 1) the playlist that the playlist source was based on
3235 is us, so just add the originals (which belonged to
3236 us anyway) back in the right place.
3238 2) the playlist that the playlist source was based on
3239 is NOT us, so we need to make copies of each of
3240 the original regions that we find, and add them
3243 bool same_playlist = (pls->original() == id());
3245 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3247 boost::shared_ptr<Region> current (*i);
3249 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3251 if (ca == cassocs.end()) {
3255 boost::shared_ptr<Region> original (ca->second);
3256 bool modified_region;
3258 if (i == rl.begin()) {
3259 move_offset = (target->position() - original->position()) - target->start();
3260 adjusted_start = original->position() + target->start();
3261 adjusted_end = adjusted_start + target->length();
3264 if (!same_playlist) {
3265 framepos_t pos = original->position();
3266 /* make a copy, but don't announce it */
3267 original = RegionFactory::create (original, false);
3268 /* the pure copy constructor resets position() to zero,
3271 original->set_position (pos);
3274 /* check to see how the original region (in the
3275 * playlist before compounding occured) overlaps
3276 * with the new state of the compound region.
3279 original->clear_changes ();
3280 modified_region = false;
3282 switch (original->coverage (adjusted_start, adjusted_end)) {
3284 /* original region does not cover any part
3285 of the current state of the compound region
3289 case OverlapInternal:
3290 /* overlap is just a small piece inside the
3291 * original so trim both ends
3293 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3294 modified_region = true;
3297 case OverlapExternal:
3298 /* overlap fully covers original, so leave it
3304 /* overlap starts within but covers end,
3305 so trim the front of the region
3307 original->trim_front (adjusted_start);
3308 modified_region = true;
3312 /* overlap covers start but ends within, so
3313 * trim the end of the region.
3315 original->trim_end (adjusted_end);
3316 modified_region = true;
3321 /* fix the position to match any movement of the compound region.
3323 original->set_position (original->position() + move_offset);
3324 modified_region = true;
3327 if (modified_region) {
3328 _session.add_command (new StatefulDiffCommand (original));
3331 /* and add to the list of regions waiting to be
3335 originals.push_back (original);
3336 old_and_new_regions.push_back (TwoRegions (*i, original));
3339 pre_uncombine (originals, target);
3341 in_partition = true;
3344 // (3) remove the compound region
3346 remove_region (target);
3348 // (4) add the constituent regions
3350 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3351 add_region ((*i), (*i)->position());
3354 /* now move dependent regions back from the compound to this playlist */
3356 pl->copy_dependents (old_and_new_regions, this);
3358 in_partition = false;
3363 Playlist::max_source_level () const
3365 RegionLock rlock (const_cast<Playlist *> (this));
3368 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3369 lvl = max (lvl, (*i)->max_source_level());
3377 Playlist::count_joined_regions () const
3379 RegionLock rlock (const_cast<Playlist *> (this));
3382 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3383 if ((*i)->max_source_level() > 0) {