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"
35 #include "pbd/stacktrace.h"
37 #include "ardour/debug.h"
38 #include "ardour/playlist.h"
39 #include "ardour/session.h"
40 #include "ardour/region.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/region_sorters.h"
43 #include "ardour/playlist_factory.h"
44 #include "ardour/playlist_source.h"
45 #include "ardour/transient_detector.h"
46 #include "ardour/session_playlists.h"
47 #include "ardour/source_factory.h"
52 using namespace ARDOUR;
56 namespace Properties {
57 PBD::PropertyDescriptor<bool> regions;
61 struct ShowMeTheList {
62 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
64 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
66 boost::shared_ptr<Playlist> playlist;
73 Playlist::make_property_quarks ()
75 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
76 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
77 Properties::regions.property_id));
80 RegionListProperty::RegionListProperty (Playlist& pl)
81 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
87 RegionListProperty::RegionListProperty (RegionListProperty const & p)
88 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
89 , _playlist (p._playlist)
95 RegionListProperty::clone () const
97 return new RegionListProperty (*this);
101 RegionListProperty::create () const
103 return new RegionListProperty (_playlist);
107 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
109 /* All regions (even those which are deleted) have their state saved by other
110 code, so we can just store ID here.
113 node.add_property ("id", region->id().to_s ());
116 boost::shared_ptr<Region>
117 RegionListProperty::get_content_from_xml (XMLNode const & node) const
119 XMLProperty const * prop = node.property ("id");
122 PBD::ID id (prop->value ());
124 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
127 ret = RegionFactory::region_by_id (id);
133 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
134 : SessionObject(sess, nom)
139 first_set_state = false;
144 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
145 : SessionObject(sess, "unnamed playlist")
150 const XMLProperty* prop = node.property("type");
151 assert(!prop || DataType(prop->value()) == _type);
155 _name = "unnamed"; /* reset by set_state */
158 /* set state called by derived class */
161 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
162 : SessionObject(other->_session, namestr)
164 , _type(other->_type)
165 , _orig_track_id (other->_orig_track_id)
170 other->copy_regions (tmp);
174 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
175 add_region_internal( (*x), (*x)->position());
180 _splicing = other->_splicing;
181 _nudging = other->_nudging;
182 _edit_mode = other->_edit_mode;
185 first_set_state = false;
187 in_partition = false;
189 _frozen = other->_frozen;
192 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
193 : SessionObject(other->_session, str)
195 , _type(other->_type)
196 , _orig_track_id (other->_orig_track_id)
198 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
200 framepos_t end = start + cnt - 1;
206 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
208 boost::shared_ptr<Region> region;
209 boost::shared_ptr<Region> new_region;
210 frameoffset_t offset = 0;
211 framepos_t position = 0;
214 Evoral::OverlapType overlap;
218 overlap = region->coverage (start, end);
221 case Evoral::OverlapNone:
224 case Evoral::OverlapInternal:
225 offset = start - region->position();
230 case Evoral::OverlapStart:
232 position = region->position() - start;
233 len = end - region->position();
236 case Evoral::OverlapEnd:
237 offset = start - region->position();
239 len = region->length() - offset;
242 case Evoral::OverlapExternal:
244 position = region->position() - start;
245 len = region->length();
249 RegionFactory::region_name (new_name, region->name(), false);
253 plist.add (Properties::start, region->start() + offset);
254 plist.add (Properties::length, len);
255 plist.add (Properties::name, new_name);
256 plist.add (Properties::layer, region->layer());
257 plist.add (Properties::layering_index, region->layering_index());
259 new_region = RegionFactory::RegionFactory::create (region, plist);
261 add_region_internal (new_region, position);
265 first_set_state = false;
272 InUse (true); /* EMIT SIGNAL */
283 InUse (false); /* EMIT SIGNAL */
288 Playlist::copy_regions (RegionList& newlist) const
290 RegionLock rlock (const_cast<Playlist *> (this));
292 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
293 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
298 Playlist::init (bool hide)
300 add_property (regions);
301 _xml_node_name = X_("Playlist");
303 g_atomic_int_set (&block_notifications, 0);
304 g_atomic_int_set (&ignore_state_changes, 0);
305 pending_contents_change = false;
306 pending_layering = false;
307 first_set_state = true;
315 _edit_mode = Config->get_edit_mode();
317 in_partition = false;
322 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
323 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
325 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
328 Playlist::~Playlist ()
330 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
333 RegionLock rl (this);
335 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
336 (*i)->set_playlist (boost::shared_ptr<Playlist>());
340 /* GoingAway must be emitted by derived classes */
344 Playlist::_set_sort_id ()
347 Playlists are given names like <track name>.<id>
348 or <track name>.<edit group name>.<id> where id
349 is an integer. We extract the id and sort by that.
352 size_t dot_position = _name.val().find_last_of(".");
354 if (dot_position == string::npos) {
357 string t = _name.val().substr(dot_position + 1);
360 _sort_id = boost::lexical_cast<int>(t);
363 catch (boost::bad_lexical_cast e) {
370 Playlist::set_name (const string& str)
372 /* in a typical situation, a playlist is being used
373 by one diskstream and also is referenced by the
374 Session. if there are more references than that,
375 then don't change the name.
382 bool ret = SessionObject::set_name(str);
389 /***********************************************************************
390 CHANGE NOTIFICATION HANDLING
392 Notifications must be delayed till the region_lock is released. This
393 is necessary because handlers for the signals may need to acquire
394 the lock (e.g. to read from the playlist).
395 ***********************************************************************/
398 Playlist::begin_undo ()
405 Playlist::end_undo ()
414 delay_notifications ();
415 g_atomic_int_inc (&ignore_state_changes);
418 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
420 Playlist::thaw (bool from_undo)
422 g_atomic_int_dec_and_test (&ignore_state_changes);
423 release_notifications (from_undo);
428 Playlist::delay_notifications ()
430 g_atomic_int_inc (&block_notifications);
433 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
435 Playlist::release_notifications (bool from_undo)
437 if (g_atomic_int_dec_and_test (&block_notifications)) {
438 flush_notifications (from_undo);
443 Playlist::notify_contents_changed ()
445 if (holding_state ()) {
446 pending_contents_change = true;
448 pending_contents_change = false;
449 ContentsChanged(); /* EMIT SIGNAL */
454 Playlist::notify_layering_changed ()
456 if (holding_state ()) {
457 pending_layering = true;
459 pending_layering = false;
460 LayeringChanged(); /* EMIT SIGNAL */
465 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
467 if (holding_state ()) {
468 pending_removes.insert (r);
469 pending_contents_change = true;
471 /* this might not be true, but we have to act
472 as though it could be.
474 pending_contents_change = false;
475 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
476 ContentsChanged (); /* EMIT SIGNAL */
481 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
483 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
485 if (holding_state ()) {
487 pending_range_moves.push_back (move);
491 list< Evoral::RangeMove<framepos_t> > m;
493 RangesMoved (m, false);
499 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
501 if (r->position() >= r->last_position()) {
502 /* trimmed shorter */
506 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
508 if (holding_state ()) {
510 pending_region_extensions.push_back (extra);
514 list<Evoral::Range<framepos_t> > r;
522 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
524 if (r->length() < r->last_length()) {
525 /* trimmed shorter */
528 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
530 if (holding_state ()) {
532 pending_region_extensions.push_back (extra);
536 list<Evoral::Range<framepos_t> > r;
544 Playlist::notify_region_added (boost::shared_ptr<Region> r)
546 /* the length change might not be true, but we have to act
547 as though it could be.
550 if (holding_state()) {
551 pending_adds.insert (r);
552 pending_contents_change = true;
555 pending_contents_change = false;
556 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
557 ContentsChanged (); /* EMIT SIGNAL */
561 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
563 Playlist::flush_notifications (bool from_undo)
565 set<boost::shared_ptr<Region> >::iterator s;
566 bool regions_changed = false;
574 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
575 regions_changed = true;
578 /* XXX: it'd be nice if we could use pending_bounds for
579 RegionsExtended and RegionsMoved.
582 /* we have no idea what order the regions ended up in pending
583 bounds (it could be based on selection order, for example).
584 so, to preserve layering in the "most recently moved is higher"
585 model, sort them by existing layer, then timestamp them.
588 // RegionSortByLayer cmp;
589 // pending_bounds.sort (cmp);
591 list<Evoral::Range<framepos_t> > crossfade_ranges;
593 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
594 crossfade_ranges.push_back ((*r)->last_range ());
595 crossfade_ranges.push_back ((*r)->range ());
598 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
599 crossfade_ranges.push_back ((*s)->range ());
600 remove_dependents (*s);
601 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
604 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
605 crossfade_ranges.push_back ((*s)->range ());
606 /* don't emit RegionAdded signal until relayering is done,
607 so that the region is fully setup by the time
608 anyone hears that its been added
612 if (((regions_changed || pending_contents_change) && !in_set_state) || pending_layering) {
616 if (regions_changed || pending_contents_change) {
617 pending_contents_change = false;
618 ContentsChanged (); /* EMIT SIGNAL */
621 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
622 (*s)->clear_changes ();
623 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
626 coalesce_and_check_crossfades (crossfade_ranges);
628 if (!pending_range_moves.empty ()) {
629 /* We don't need to check crossfades for these as pending_bounds has
632 RangesMoved (pending_range_moves, from_undo);
635 if (!pending_region_extensions.empty ()) {
636 RegionsExtended (pending_region_extensions);
645 Playlist::clear_pending ()
647 pending_adds.clear ();
648 pending_removes.clear ();
649 pending_bounds.clear ();
650 pending_range_moves.clear ();
651 pending_region_extensions.clear ();
652 pending_contents_change = false;
655 /*************************************************************
657 *************************************************************/
659 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
661 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
663 RegionLock rlock (this);
664 times = fabs (times);
666 int itimes = (int) floor (times);
668 framepos_t pos = position;
670 if (times == 1 && auto_partition){
671 partition(pos - 1, (pos + region->length()), true);
675 add_region_internal (region, pos);
676 set_layer (region, DBL_MAX);
677 pos += region->length();
682 /* note that itimes can be zero if we being asked to just
683 insert a single fraction of the region.
686 for (int i = 0; i < itimes; ++i) {
687 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
688 add_region_internal (copy, pos);
689 set_layer (copy, DBL_MAX);
690 pos += region->length();
693 framecnt_t length = 0;
695 if (floor (times) != times) {
696 length = (framecnt_t) floor (region->length() * (times - floor (times)));
698 RegionFactory::region_name (name, region->name(), false);
703 plist.add (Properties::start, region->start());
704 plist.add (Properties::length, length);
705 plist.add (Properties::name, name);
706 plist.add (Properties::layer, region->layer());
708 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
709 add_region_internal (sub, pos);
710 set_layer (sub, DBL_MAX);
714 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
718 Playlist::set_region_ownership ()
720 RegionLock rl (this);
721 RegionList::iterator i;
722 boost::weak_ptr<Playlist> pl (shared_from_this());
724 for (i = regions.begin(); i != regions.end(); ++i) {
725 (*i)->set_playlist (pl);
730 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
732 if (region->data_type() != _type) {
736 RegionSortByPosition cmp;
738 if (!first_set_state) {
739 boost::shared_ptr<Playlist> foo (shared_from_this());
740 region->set_playlist (boost::weak_ptr<Playlist>(foo));
743 region->set_position (position);
745 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
746 all_regions.insert (region);
748 possibly_splice_unlocked (position, region->length(), region);
750 if (!holding_state ()) {
751 /* layers get assigned from XML state, and are not reset during undo/redo */
755 /* we need to notify the existence of new region before checking dependents. Ick. */
757 notify_region_added (region);
759 if (!holding_state ()) {
760 check_crossfades (region->range ());
763 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
769 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
771 RegionLock rlock (this);
773 bool old_sp = _splicing;
776 remove_region_internal (old);
777 add_region_internal (newr, pos);
778 set_layer (newr, old->layer ());
782 possibly_splice_unlocked (pos, old->length() - newr->length());
786 Playlist::remove_region (boost::shared_ptr<Region> region)
788 RegionLock rlock (this);
789 remove_region_internal (region);
793 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
795 RegionList::iterator i;
799 region->set_playlist (boost::weak_ptr<Playlist>());
802 /* XXX should probably freeze here .... */
804 for (i = regions.begin(); i != regions.end(); ++i) {
807 framepos_t pos = (*i)->position();
808 framecnt_t distance = (*i)->length();
812 possibly_splice_unlocked (pos, -distance);
814 if (!holding_state ()) {
816 remove_dependents (region);
819 notify_region_removed (region);
828 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
830 if (Config->get_use_overlap_equivalency()) {
831 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
832 if ((*i)->overlap_equivalent (other)) {
833 results.push_back (*i);
837 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
838 if ((*i)->equivalent (other)) {
839 results.push_back (*i);
846 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
848 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
850 if ((*i) && (*i)->region_list_equivalent (other)) {
851 results.push_back (*i);
857 Playlist::partition (framepos_t start, framepos_t end, bool cut)
861 partition_internal (start, end, cut, thawlist);
863 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
864 (*i)->resume_property_changes ();
868 /** Go through each region on the playlist and cut them at start and end, removing the section between
869 * start and end if cutting == true. Regions that lie entirely within start and end are always
874 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
876 RegionList new_regions;
879 RegionLock rlock (this);
881 boost::shared_ptr<Region> region;
882 boost::shared_ptr<Region> current;
884 RegionList::iterator tmp;
885 Evoral::OverlapType overlap;
886 framepos_t pos1, pos2, pos3, pos4;
890 /* need to work from a copy, because otherwise the regions we add during the process
891 get operated on as well.
894 RegionList copy = regions.rlist();
896 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
903 if (current->first_frame() >= start && current->last_frame() < end) {
906 remove_region_internal (current);
912 /* coverage will return OverlapStart if the start coincides
913 with the end point. we do not partition such a region,
914 so catch this special case.
917 if (current->first_frame() >= end) {
921 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
925 pos1 = current->position();
928 pos4 = current->last_frame();
930 if (overlap == Evoral::OverlapInternal) {
931 /* split: we need 3 new regions, the front, middle and end.
932 cut: we need 2 regions, the front and end.
937 ---------------*************************------------
940 ---------------*****++++++++++++++++====------------
942 ---------------*****----------------====------------
947 /* "middle" ++++++ */
949 RegionFactory::region_name (new_name, current->name(), false);
953 plist.add (Properties::start, current->start() + (pos2 - pos1));
954 plist.add (Properties::length, pos3 - pos2);
955 plist.add (Properties::name, new_name);
956 plist.add (Properties::layer, current->layer ());
957 plist.add (Properties::layering_index, current->layering_index ());
958 plist.add (Properties::automatic, true);
959 plist.add (Properties::left_of_split, true);
960 plist.add (Properties::right_of_split, true);
962 region = RegionFactory::create (current, plist);
963 add_region_internal (region, start);
964 new_regions.push_back (region);
969 RegionFactory::region_name (new_name, current->name(), false);
973 plist.add (Properties::start, current->start() + (pos3 - pos1));
974 plist.add (Properties::length, pos4 - pos3);
975 plist.add (Properties::name, new_name);
976 plist.add (Properties::layer, current->layer ());
977 plist.add (Properties::layering_index, current->layering_index ());
978 plist.add (Properties::automatic, true);
979 plist.add (Properties::right_of_split, true);
981 region = RegionFactory::create (current, plist);
983 add_region_internal (region, end);
984 new_regions.push_back (region);
988 current->suspend_property_changes ();
989 thawlist.push_back (current);
990 current->cut_end (pos2 - 1);
992 } else if (overlap == Evoral::OverlapEnd) {
996 ---------------*************************------------
999 ---------------**************+++++++++++------------
1001 ---------------**************-----------------------
1008 RegionFactory::region_name (new_name, current->name(), false);
1012 plist.add (Properties::start, current->start() + (pos2 - pos1));
1013 plist.add (Properties::length, pos4 - pos2);
1014 plist.add (Properties::name, new_name);
1015 plist.add (Properties::layer, current->layer ());
1016 plist.add (Properties::layering_index, current->layering_index ());
1017 plist.add (Properties::automatic, true);
1018 plist.add (Properties::left_of_split, true);
1020 region = RegionFactory::create (current, plist);
1022 add_region_internal (region, start);
1023 new_regions.push_back (region);
1028 current->suspend_property_changes ();
1029 thawlist.push_back (current);
1030 current->cut_end (pos2 - 1);
1032 } else if (overlap == Evoral::OverlapStart) {
1034 /* split: we need 2 regions: the front and the end.
1035 cut: just trim current to skip the cut area
1040 ---------------*************************------------
1044 ---------------****+++++++++++++++++++++------------
1046 -------------------*********************------------
1052 RegionFactory::region_name (new_name, current->name(), false);
1056 plist.add (Properties::start, current->start());
1057 plist.add (Properties::length, pos3 - pos1);
1058 plist.add (Properties::name, new_name);
1059 plist.add (Properties::layer, current->layer ());
1060 plist.add (Properties::layering_index, current->layering_index ());
1061 plist.add (Properties::automatic, true);
1062 plist.add (Properties::right_of_split, true);
1064 region = RegionFactory::create (current, plist);
1066 add_region_internal (region, pos1);
1067 new_regions.push_back (region);
1072 current->suspend_property_changes ();
1073 thawlist.push_back (current);
1074 current->trim_front (pos3);
1075 } else if (overlap == Evoral::OverlapExternal) {
1077 /* split: no split required.
1078 cut: remove the region.
1083 ---------------*************************------------
1087 ---------------*************************------------
1089 ----------------------------------------------------
1094 remove_region_internal (current);
1097 new_regions.push_back (current);
1101 in_partition = false;
1104 check_crossfades (Evoral::Range<framepos_t> (start, end));
1107 boost::shared_ptr<Playlist>
1108 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1110 boost::shared_ptr<Playlist> ret;
1111 boost::shared_ptr<Playlist> pl;
1114 if (ranges.empty()) {
1115 return boost::shared_ptr<Playlist>();
1118 start = ranges.front().start;
1120 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1122 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1124 if (i == ranges.begin()) {
1128 /* paste the next section into the nascent playlist,
1129 offset to reflect the start of the first range we
1133 ret->paste (pl, (*i).start - start, 1.0f);
1140 boost::shared_ptr<Playlist>
1141 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1143 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1144 return cut_copy (pmf, ranges, result_is_hidden);
1147 boost::shared_ptr<Playlist>
1148 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1150 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1151 return cut_copy (pmf, ranges, result_is_hidden);
1154 boost::shared_ptr<Playlist>
1155 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1157 boost::shared_ptr<Playlist> the_copy;
1158 RegionList thawlist;
1161 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1162 string new_name = _name;
1166 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1167 return boost::shared_ptr<Playlist>();
1170 partition_internal (start, start+cnt-1, true, thawlist);
1172 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1173 (*i)->resume_property_changes();
1179 boost::shared_ptr<Playlist>
1180 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1184 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1185 string new_name = _name;
1189 cnt = min (_get_extent().second - start, cnt);
1190 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1194 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1196 times = fabs (times);
1199 RegionLock rl1 (this);
1200 RegionLock rl2 (other.get());
1202 int itimes = (int) floor (times);
1203 framepos_t pos = position;
1204 framecnt_t const shift = other->_get_extent().second;
1205 layer_t top = top_layer ();
1208 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1209 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1211 /* put these new regions on top of all existing ones, but preserve
1212 the ordering they had in the original playlist.
1215 add_region_internal (copy_of_region, (*i)->position() + pos);
1216 set_layer (copy_of_region, copy_of_region->layer() + top);
1227 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1229 times = fabs (times);
1231 RegionLock rl (this);
1232 int itimes = (int) floor (times);
1233 framepos_t pos = position + 1;
1236 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1237 add_region_internal (copy, pos);
1238 set_layer (copy, DBL_MAX);
1239 pos += region->length();
1242 if (floor (times) != times) {
1243 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1245 RegionFactory::region_name (name, region->name(), false);
1250 plist.add (Properties::start, region->start());
1251 plist.add (Properties::length, length);
1252 plist.add (Properties::name, name);
1254 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1255 add_region_internal (sub, pos);
1256 set_layer (sub, DBL_MAX);
1262 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1264 RegionLock rlock (this);
1265 RegionList copy (regions.rlist());
1268 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1270 if ((*r)->last_frame() < at) {
1275 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1276 /* intersected region */
1277 if (!move_intersected) {
1282 /* do not move regions glued to music time - that
1283 has to be done separately.
1286 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1287 fixup.push_back (*r);
1291 (*r)->set_position ((*r)->position() + distance);
1294 /* XXX: may not be necessary; Region::post_set should do this, I think */
1295 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1296 (*r)->recompute_position_from_lock_style ();
1301 Playlist::split (framepos_t at)
1303 RegionLock rlock (this);
1304 RegionList copy (regions.rlist());
1306 /* use a copy since this operation can modify the region list
1309 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1310 _split_region (*r, at);
1315 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1317 RegionLock rl (this);
1318 _split_region (region, playlist_position);
1322 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1324 if (!region->covers (playlist_position)) {
1328 if (region->position() == playlist_position ||
1329 region->last_frame() == playlist_position) {
1333 boost::shared_ptr<Region> left;
1334 boost::shared_ptr<Region> right;
1335 frameoffset_t before;
1336 frameoffset_t after;
1340 /* split doesn't change anything about length, so don't try to splice */
1342 bool old_sp = _splicing;
1345 before = playlist_position - region->position();
1346 after = region->length() - before;
1348 RegionFactory::region_name (before_name, region->name(), false);
1353 plist.add (Properties::position, region->position ());
1354 plist.add (Properties::length, before);
1355 plist.add (Properties::name, before_name);
1356 plist.add (Properties::left_of_split, true);
1357 plist.add (Properties::layering_index, region->layering_index ());
1358 plist.add (Properties::layer, region->layer ());
1360 /* note: we must use the version of ::create with an offset here,
1361 since it supplies that offset to the Region constructor, which
1362 is necessary to get audio region gain envelopes right.
1364 left = RegionFactory::create (region, 0, plist);
1367 RegionFactory::region_name (after_name, region->name(), false);
1372 plist.add (Properties::position, region->position() + before);
1373 plist.add (Properties::length, after);
1374 plist.add (Properties::name, after_name);
1375 plist.add (Properties::right_of_split, true);
1376 plist.add (Properties::layering_index, region->layering_index ());
1377 plist.add (Properties::layer, region->layer ());
1379 /* same note as above */
1380 right = RegionFactory::create (region, before, plist);
1383 add_region_internal (left, region->position());
1384 add_region_internal (right, region->position() + before);
1385 remove_region_internal (region);
1391 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1393 if (_splicing || in_set_state) {
1394 /* don't respond to splicing moves or state setting */
1398 if (_edit_mode == Splice) {
1399 splice_locked (at, distance, exclude);
1404 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1406 if (_splicing || in_set_state) {
1407 /* don't respond to splicing moves or state setting */
1411 if (_edit_mode == Splice) {
1412 splice_unlocked (at, distance, exclude);
1417 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1420 RegionLock rl (this);
1421 core_splice (at, distance, exclude);
1426 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1428 core_splice (at, distance, exclude);
1432 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1436 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1438 if (exclude && (*i) == exclude) {
1442 if ((*i)->position() >= at) {
1443 framepos_t new_pos = (*i)->position() + distance;
1446 } else if (new_pos >= max_framepos - (*i)->length()) {
1447 new_pos = max_framepos - (*i)->length();
1450 (*i)->set_position (new_pos);
1456 notify_contents_changed ();
1460 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1462 if (in_set_state || _splicing || _nudging || _shuffling) {
1466 if (what_changed.contains (Properties::position)) {
1468 /* remove it from the list then add it back in
1469 the right place again.
1472 RegionSortByPosition cmp;
1474 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1476 if (i == regions.end()) {
1477 /* the region bounds are being modified but its not currently
1478 in the region list. we will use its bounds correctly when/if
1485 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1488 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1490 frameoffset_t delta = 0;
1492 if (what_changed.contains (Properties::position)) {
1493 delta = region->position() - region->last_position();
1496 if (what_changed.contains (Properties::length)) {
1497 delta += region->length() - region->last_length();
1501 possibly_splice (region->last_position() + region->last_length(), delta, region);
1504 if (holding_state ()) {
1505 pending_bounds.push_back (region);
1507 notify_contents_changed ();
1509 list<Evoral::Range<framepos_t> > xf;
1510 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1511 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1512 coalesce_and_check_crossfades (xf);
1518 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1520 boost::shared_ptr<Region> region (weak_region.lock());
1526 /* this makes a virtual call to the right kind of playlist ... */
1528 region_changed (what_changed, region);
1532 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1534 PropertyChange our_interests;
1535 PropertyChange bounds;
1536 PropertyChange pos_and_length;
1539 if (in_set_state || in_flush) {
1543 our_interests.add (Properties::muted);
1544 our_interests.add (Properties::layer);
1545 our_interests.add (Properties::opaque);
1547 bounds.add (Properties::start);
1548 bounds.add (Properties::position);
1549 bounds.add (Properties::length);
1551 pos_and_length.add (Properties::position);
1552 pos_and_length.add (Properties::length);
1554 if (what_changed.contains (bounds)) {
1555 region_bounds_changed (what_changed, region);
1556 save = !(_splicing || _nudging);
1559 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1560 check_crossfades (region->range ());
1563 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1564 notify_region_moved (region);
1565 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1566 notify_region_end_trimmed (region);
1567 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1568 notify_region_start_trimmed (region);
1571 /* don't notify about layer changes, since we are the only object that can initiate
1572 them, and we notify in ::relayer()
1575 if (what_changed.contains (our_interests)) {
1583 Playlist::drop_regions ()
1585 RegionLock rl (this);
1587 all_regions.clear ();
1591 Playlist::sync_all_regions_with_regions ()
1593 RegionLock rl (this);
1595 all_regions.clear ();
1597 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1598 all_regions.insert (*i);
1603 Playlist::clear (bool with_signals)
1606 RegionLock rl (this);
1608 region_state_changed_connections.drop_connections ();
1610 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1611 pending_removes.insert (*i);
1616 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1617 remove_dependents (*s);
1623 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1624 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1627 pending_removes.clear ();
1628 pending_contents_change = false;
1634 /***********************************************************************
1636 **********************************************************************/
1638 boost::shared_ptr<RegionList>
1639 Playlist::regions_at (framepos_t frame)
1641 RegionLock rlock (this);
1642 return find_regions_at (frame);
1646 Playlist::count_regions_at (framepos_t frame) const
1648 RegionLock rlock (const_cast<Playlist*>(this));
1651 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1652 if ((*i)->covers (frame)) {
1660 boost::shared_ptr<Region>
1661 Playlist::top_region_at (framepos_t frame)
1664 RegionLock rlock (this);
1665 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1666 boost::shared_ptr<Region> region;
1668 if (rlist->size()) {
1669 RegionSortByLayer cmp;
1671 region = rlist->back();
1677 boost::shared_ptr<Region>
1678 Playlist::top_unmuted_region_at (framepos_t frame)
1681 RegionLock rlock (this);
1682 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1684 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1686 RegionList::iterator tmp = i;
1689 if ((*i)->muted()) {
1696 boost::shared_ptr<Region> region;
1698 if (rlist->size()) {
1699 RegionSortByLayer cmp;
1701 region = rlist->back();
1707 boost::shared_ptr<RegionList>
1708 Playlist::find_regions_at (framepos_t frame)
1710 /* Caller must hold lock */
1712 boost::shared_ptr<RegionList> rlist (new RegionList);
1714 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1715 if ((*i)->covers (frame)) {
1716 rlist->push_back (*i);
1723 boost::shared_ptr<RegionList>
1724 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1726 RegionLock rlock (this);
1727 boost::shared_ptr<RegionList> rlist (new RegionList);
1729 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1730 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1731 rlist->push_back (*i);
1738 boost::shared_ptr<RegionList>
1739 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1741 RegionLock rlock (this);
1742 boost::shared_ptr<RegionList> rlist (new RegionList);
1744 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1745 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1746 rlist->push_back (*i);
1753 /** @param start Range start.
1754 * @param end Range end.
1755 * @return regions which have some part within this range.
1757 boost::shared_ptr<RegionList>
1758 Playlist::regions_touched (framepos_t start, framepos_t end)
1760 RegionLock rlock (this);
1761 boost::shared_ptr<RegionList> rlist (new RegionList);
1763 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1764 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1765 rlist->push_back (*i);
1773 Playlist::find_next_transient (framepos_t from, int dir)
1775 RegionLock rlock (this);
1776 AnalysisFeatureList points;
1777 AnalysisFeatureList these_points;
1779 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1781 if ((*i)->last_frame() < from) {
1785 if ((*i)->first_frame() > from) {
1790 (*i)->get_transients (these_points);
1792 /* add first frame, just, err, because */
1794 these_points.push_back ((*i)->first_frame());
1796 points.insert (points.end(), these_points.begin(), these_points.end());
1797 these_points.clear ();
1800 if (points.empty()) {
1804 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1805 bool reached = false;
1808 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1813 if (reached && (*x) > from) {
1818 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1823 if (reached && (*x) < from) {
1832 boost::shared_ptr<Region>
1833 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1835 RegionLock rlock (this);
1836 boost::shared_ptr<Region> ret;
1837 framepos_t closest = max_framepos;
1839 bool end_iter = false;
1841 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1845 frameoffset_t distance;
1846 boost::shared_ptr<Region> r = (*i);
1851 pos = r->first_frame ();
1854 pos = r->last_frame ();
1857 pos = r->sync_position ();
1862 case 1: /* forwards */
1865 if ((distance = pos - frame) < closest) {
1874 default: /* backwards */
1877 if ((distance = frame - pos) < closest) {
1894 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1896 RegionLock rlock (this);
1898 framepos_t closest = max_framepos;
1899 framepos_t ret = -1;
1903 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1905 boost::shared_ptr<Region> r = (*i);
1906 frameoffset_t distance;
1908 if (r->first_frame() > frame) {
1910 distance = r->first_frame() - frame;
1912 if (distance < closest) {
1913 ret = r->first_frame();
1918 if (r->last_frame () > frame) {
1920 distance = r->last_frame () - frame;
1922 if (distance < closest) {
1923 ret = r->last_frame ();
1931 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1933 boost::shared_ptr<Region> r = (*i);
1934 frameoffset_t distance;
1936 if (r->last_frame() < frame) {
1938 distance = frame - r->last_frame();
1940 if (distance < closest) {
1941 ret = r->last_frame();
1946 if (r->first_frame() < frame) {
1948 distance = frame - r->first_frame();
1950 if (distance < closest) {
1951 ret = r->first_frame();
1962 /***********************************************************************/
1968 Playlist::mark_session_dirty ()
1970 if (!in_set_state && !holding_state ()) {
1971 _session.set_dirty();
1976 Playlist::rdiff (vector<Command*>& cmds) const
1978 RegionLock rlock (const_cast<Playlist *> (this));
1979 Stateful::rdiff (cmds);
1983 Playlist::clear_owned_changes ()
1985 RegionLock rlock (this);
1986 Stateful::clear_owned_changes ();
1990 Playlist::update (const RegionListProperty::ChangeRecord& change)
1992 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
1993 name(), change.added.size(), change.removed.size()));
1996 /* add the added regions */
1997 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
1998 add_region_internal ((*i), (*i)->position());
2000 /* remove the removed regions */
2001 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2009 Playlist::set_state (const XMLNode& node, int version)
2013 XMLNodeConstIterator niter;
2014 XMLPropertyList plist;
2015 XMLPropertyConstIterator piter;
2017 boost::shared_ptr<Region> region;
2019 bool seen_region_nodes = false;
2024 if (node.name() != "Playlist") {
2031 plist = node.properties();
2035 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2039 if (prop->name() == X_("name")) {
2040 _name = prop->value();
2042 } else if (prop->name() == X_("orig-diskstream-id")) {
2043 /* XXX legacy session: fix up later */
2044 _orig_track_id = prop->value ();
2045 } else if (prop->name() == X_("orig-track-id")) {
2046 _orig_track_id = prop->value ();
2047 } else if (prop->name() == X_("frozen")) {
2048 _frozen = string_is_affirmative (prop->value());
2049 } else if (prop->name() == X_("combine-ops")) {
2050 _combine_ops = atoi (prop->value());
2056 nlist = node.children();
2058 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2062 if (child->name() == "Region") {
2064 seen_region_nodes = true;
2066 if ((prop = child->property ("id")) == 0) {
2067 error << _("region state node has no ID, ignored") << endmsg;
2071 ID id = prop->value ();
2073 if ((region = region_by_id (id))) {
2075 region->suspend_property_changes ();
2077 if (region->set_state (*child, version)) {
2078 region->resume_property_changes ();
2082 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2083 region->suspend_property_changes ();
2085 error << _("Playlist: cannot create region from XML") << endmsg;
2090 RegionLock rlock (this);
2091 add_region_internal (region, region->position());
2094 region->resume_property_changes ();
2099 if (seen_region_nodes && regions.empty()) {
2103 /* update dependents, which was not done during add_region_internal
2104 due to in_set_state being true
2107 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2108 check_crossfades ((*r)->range ());
2113 notify_contents_changed ();
2116 first_set_state = false;
2122 Playlist::get_state()
2124 return state (true);
2128 Playlist::get_template()
2130 return state (false);
2133 /** @param full_state true to include regions in the returned state, otherwise false.
2136 Playlist::state (bool full_state)
2138 XMLNode *node = new XMLNode (X_("Playlist"));
2141 node->add_property (X_("id"), id().to_s());
2142 node->add_property (X_("name"), _name);
2143 node->add_property (X_("type"), _type.to_string());
2145 _orig_track_id.print (buf, sizeof (buf));
2146 node->add_property (X_("orig-track-id"), buf);
2147 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2150 RegionLock rlock (this, false);
2152 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2153 node->add_property ("combine-ops", buf);
2155 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2156 node->add_child_nocopy ((*i)->get_state());
2161 node->add_child_copy (*_extra_xml);
2168 Playlist::empty() const
2170 RegionLock rlock (const_cast<Playlist *>(this), false);
2171 return regions.empty();
2175 Playlist::n_regions() const
2177 RegionLock rlock (const_cast<Playlist *>(this), false);
2178 return regions.size();
2181 pair<framepos_t, framepos_t>
2182 Playlist::get_extent () const
2184 RegionLock rlock (const_cast<Playlist *>(this), false);
2185 return _get_extent ();
2188 pair<framepos_t, framepos_t>
2189 Playlist::_get_extent () const
2191 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2193 if (regions.empty()) {
2198 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2199 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2200 if (e.first < ext.first) {
2201 ext.first = e.first;
2203 if (e.second > ext.second) {
2204 ext.second = e.second;
2212 Playlist::bump_name (string name, Session &session)
2214 string newname = name;
2217 newname = bump_name_once (newname, '.');
2218 } while (session.playlists->by_name (newname)!=NULL);
2225 Playlist::top_layer() const
2227 RegionLock rlock (const_cast<Playlist *> (this));
2230 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2231 top = max (top, (*i)->layer());
2237 Playlist::set_edit_mode (EditMode mode)
2242 struct RelayerSort {
2243 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2244 return a->layering_index() < b->layering_index();
2248 /** Set a new layer for a region. This adjusts the layering indices of all
2249 * regions in the playlist to put the specified region in the appropriate
2250 * place. The actual layering will be fixed up when relayer() happens.
2254 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2256 /* Remove the layer we are setting from our region list, and sort it */
2257 RegionList copy = regions.rlist();
2258 copy.remove (region);
2259 copy.sort (RelayerSort ());
2261 /* Put region back in the right place */
2262 RegionList::iterator i = copy.begin();
2263 while (i != copy.end ()) {
2264 if ((*i)->layer() > new_layer) {
2270 copy.insert (i, region);
2272 setup_layering_indices (copy);
2276 Playlist::setup_layering_indices (RegionList const & regions)
2279 list<Evoral::Range<framepos_t> > xf;
2281 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2282 (*k)->set_layering_index (j++);
2286 /** Take the layering indices of each of our regions, compute the layers
2287 * that they should be on, and write the layers back to the regions.
2290 Playlist::relayer ()
2292 /* never compute layers when setting from XML */
2298 /* Build up a new list of regions on each layer, stored in a set of lists
2299 each of which represent some period of time on some layer. The idea
2300 is to avoid having to search the entire region list to establish whether
2301 each region overlaps another */
2303 /* how many pieces to divide this playlist's time up into */
2304 int const divisions = 512;
2306 /* find the start and end positions of the regions on this playlist */
2307 framepos_t start = INT64_MAX;
2309 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2310 start = min (start, (*i)->position());
2311 end = max (end, (*i)->position() + (*i)->length());
2314 /* hence the size of each time division */
2315 double const division_size = (end - start) / double (divisions);
2317 vector<vector<RegionList> > layers;
2318 layers.push_back (vector<RegionList> (divisions));
2320 /* Sort our regions into layering index order */
2321 RegionList copy = regions.rlist();
2322 copy.sort (RelayerSort ());
2324 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2325 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2326 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2329 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2331 /* find the time divisions that this region covers; if there are no regions on the list,
2332 division_size will equal 0 and in this case we'll just say that
2333 start_division = end_division = 0.
2335 int start_division = 0;
2336 int end_division = 0;
2338 if (division_size > 0) {
2339 start_division = floor ( ((*i)->position() - start) / division_size);
2340 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2341 if (end_division == divisions) {
2346 assert (divisions == 0 || end_division < divisions);
2348 /* find the lowest layer that this region can go on */
2349 size_t j = layers.size();
2351 /* try layer j - 1; it can go on if it overlaps no other region
2352 that is already on that layer
2355 bool overlap = false;
2356 for (int k = start_division; k <= end_division; ++k) {
2357 RegionList::iterator l = layers[j-1][k].begin ();
2358 while (l != layers[j-1][k].end()) {
2359 if ((*l)->overlap_equivalent (*i)) {
2372 /* overlap, so we must use layer j */
2379 if (j == layers.size()) {
2380 /* we need a new layer for this region */
2381 layers.push_back (vector<RegionList> (divisions));
2384 /* put a reference to this region in each of the divisions that it exists in */
2385 for (int k = start_division; k <= end_division; ++k) {
2386 layers[j][k].push_back (*i);
2389 (*i)->set_layer (j);
2392 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2393 relayering because we just removed the only region on the top layer, nothing will
2394 appear to have changed, but the StreamView must still sort itself out. We could
2395 probably keep a note of the top layer last time we relayered, and check that,
2396 but premature optimisation &c...
2398 notify_layering_changed ();
2400 /* This relayer() may have been called as a result of a region removal, in which
2401 case we need to setup layering indices to account for the one that has just
2404 setup_layering_indices (copy);
2408 Playlist::raise_region (boost::shared_ptr<Region> region)
2410 set_layer (region, region->layer() + 1.5);
2412 check_crossfades (region->range ());
2416 Playlist::lower_region (boost::shared_ptr<Region> region)
2418 set_layer (region, region->layer() - 1.5);
2420 check_crossfades (region->range ());
2424 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2426 set_layer (region, DBL_MAX);
2428 check_crossfades (region->range ());
2432 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2434 set_layer (region, -0.5);
2436 check_crossfades (region->range ());
2440 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2442 RegionList::iterator i;
2448 RegionLock rlock (const_cast<Playlist *> (this));
2450 for (i = regions.begin(); i != regions.end(); ++i) {
2452 if ((*i)->position() >= start) {
2458 if ((*i)->last_frame() > max_framepos - distance) {
2459 new_pos = max_framepos - (*i)->length();
2461 new_pos = (*i)->position() + distance;
2466 if ((*i)->position() > distance) {
2467 new_pos = (*i)->position() - distance;
2473 (*i)->set_position (new_pos);
2481 notify_contents_changed ();
2487 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2489 RegionLock rlock (const_cast<Playlist*> (this));
2491 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2492 if ((*r)->uses_source (src)) {
2500 boost::shared_ptr<Region>
2501 Playlist::find_region (const ID& id) const
2503 RegionLock rlock (const_cast<Playlist*> (this));
2505 /* searches all regions currently in use by the playlist */
2507 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2508 if ((*i)->id() == id) {
2513 return boost::shared_ptr<Region> ();
2517 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2519 RegionLock rlock (const_cast<Playlist*> (this));
2522 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2531 boost::shared_ptr<Region>
2532 Playlist::region_by_id (const ID& id) const
2534 /* searches all regions ever added to this playlist */
2536 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2537 if ((*i)->id() == id) {
2541 return boost::shared_ptr<Region> ();
2545 Playlist::dump () const
2547 boost::shared_ptr<Region> r;
2549 cerr << "Playlist \"" << _name << "\" " << endl
2550 << regions.size() << " regions "
2553 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2555 cerr << " " << r->name() << " ["
2556 << r->start() << "+" << r->length()
2566 Playlist::set_frozen (bool yn)
2572 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2576 if (region->locked()) {
2583 RegionLock rlock (const_cast<Playlist*> (this));
2588 RegionList::iterator next;
2590 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2591 if ((*i) == region) {
2595 if (next != regions.end()) {
2597 if ((*next)->locked()) {
2603 if ((*next)->position() != region->last_frame() + 1) {
2604 /* they didn't used to touch, so after shuffle,
2605 just have them swap positions.
2607 new_pos = (*next)->position();
2609 /* they used to touch, so after shuffle,
2610 make sure they still do. put the earlier
2611 region where the later one will end after
2614 new_pos = region->position() + (*next)->length();
2617 (*next)->set_position (region->position());
2618 region->set_position (new_pos);
2620 /* avoid a full sort */
2622 regions.erase (i); // removes the region from the list */
2624 regions.insert (next, region); // adds it back after next
2633 RegionList::iterator prev = regions.end();
2635 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2636 if ((*i) == region) {
2638 if (prev != regions.end()) {
2640 if ((*prev)->locked()) {
2645 if (region->position() != (*prev)->last_frame() + 1) {
2646 /* they didn't used to touch, so after shuffle,
2647 just have them swap positions.
2649 new_pos = region->position();
2651 /* they used to touch, so after shuffle,
2652 make sure they still do. put the earlier
2653 one where the later one will end after
2655 new_pos = (*prev)->position() + region->length();
2658 region->set_position ((*prev)->position());
2659 (*prev)->set_position (new_pos);
2661 /* avoid a full sort */
2663 regions.erase (i); // remove region
2664 regions.insert (prev, region); // insert region before prev
2680 notify_contents_changed();
2686 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2688 RegionLock rlock (const_cast<Playlist*> (this));
2690 if (regions.size() > 1) {
2698 Playlist::update_after_tempo_map_change ()
2700 RegionLock rlock (const_cast<Playlist*> (this));
2701 RegionList copy (regions.rlist());
2705 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2706 (*i)->update_after_tempo_map_change ();
2713 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2715 RegionLock rl (this, false);
2716 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2722 Playlist::has_region_at (framepos_t const p) const
2724 RegionLock (const_cast<Playlist *> (this));
2726 RegionList::const_iterator i = regions.begin ();
2727 while (i != regions.end() && !(*i)->covers (p)) {
2731 return (i != regions.end());
2734 /** Remove any region that uses a given source */
2736 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2738 RegionLock rl (this);
2740 RegionList::iterator i = regions.begin();
2741 while (i != regions.end()) {
2742 RegionList::iterator j = i;
2745 if ((*i)->uses_source (s)) {
2746 remove_region_internal (*i);
2753 /** Look from a session frame time and find the start time of the next region
2754 * which is on the top layer of this playlist.
2755 * @param t Time to look from.
2756 * @return Position of next top-layered region, or max_framepos if there isn't one.
2759 Playlist::find_next_top_layer_position (framepos_t t) const
2761 RegionLock rlock (const_cast<Playlist *> (this));
2763 layer_t const top = top_layer ();
2765 RegionList copy = regions.rlist ();
2766 copy.sort (RegionSortByPosition ());
2768 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2769 if ((*i)->position() >= t && (*i)->layer() == top) {
2770 return (*i)->position();
2774 return max_framepos;
2777 boost::shared_ptr<Region>
2778 Playlist::combine (const RegionList& r)
2781 uint32_t channels = 0;
2783 framepos_t earliest_position = max_framepos;
2784 vector<TwoRegions> old_and_new_regions;
2785 vector<boost::shared_ptr<Region> > originals;
2786 vector<boost::shared_ptr<Region> > copies;
2789 uint32_t max_level = 0;
2791 /* find the maximum depth of all the regions we're combining */
2793 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2794 max_level = max (max_level, (*i)->max_source_level());
2797 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2798 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2800 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2802 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2803 earliest_position = min (earliest_position, (*i)->position());
2806 /* enable this so that we do not try to create xfades etc. as we add
2810 pl->in_partition = true;
2812 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2814 /* copy the region */
2816 boost::shared_ptr<Region> original_region = (*i);
2817 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2819 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2820 originals.push_back (original_region);
2821 copies.push_back (copied_region);
2823 RegionFactory::add_compound_association (original_region, copied_region);
2825 /* make position relative to zero */
2827 pl->add_region (copied_region, original_region->position() - earliest_position);
2829 /* use the maximum number of channels for any region */
2831 channels = max (channels, original_region->n_channels());
2833 /* it will go above the layer of the highest existing region */
2835 layer = max (layer, original_region->layer());
2838 pl->in_partition = false;
2840 pre_combine (copies);
2842 /* now create a new PlaylistSource for each channel in the new playlist */
2845 pair<framepos_t,framepos_t> extent = pl->get_extent();
2847 for (uint32_t chn = 0; chn < channels; ++chn) {
2848 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2852 /* now a new whole-file region using the list of sources */
2854 plist.add (Properties::start, 0);
2855 plist.add (Properties::length, extent.second);
2856 plist.add (Properties::name, parent_name);
2857 plist.add (Properties::whole_file, true);
2859 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2861 /* now the non-whole-file region that we will actually use in the
2866 plist.add (Properties::start, 0);
2867 plist.add (Properties::length, extent.second);
2868 plist.add (Properties::name, child_name);
2869 plist.add (Properties::layer, layer+1);
2871 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2873 /* remove all the selected regions from the current playlist
2878 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2882 /* do type-specific stuff with the originals and the new compound
2886 post_combine (originals, compound_region);
2888 /* add the new region at the right location */
2890 add_region (compound_region, earliest_position);
2896 return compound_region;
2900 Playlist::uncombine (boost::shared_ptr<Region> target)
2902 boost::shared_ptr<PlaylistSource> pls;
2903 boost::shared_ptr<const Playlist> pl;
2904 vector<boost::shared_ptr<Region> > originals;
2905 vector<TwoRegions> old_and_new_regions;
2907 // (1) check that its really a compound region
2909 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
2913 pl = pls->playlist();
2915 framepos_t adjusted_start = 0; // gcc isn't smart enough
2916 framepos_t adjusted_end = 0; // gcc isn't smart enough
2918 /* the leftmost (earliest) edge of the compound region
2919 starts at zero in its source, or larger if it
2920 has been trimmed or content-scrolled.
2922 the rightmost (latest) edge of the compound region
2923 relative to its source is the starting point plus
2924 the length of the region.
2927 // (2) get all the original regions
2929 const RegionList& rl (pl->region_list().rlist());
2930 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2931 frameoffset_t move_offset = 0;
2933 /* there are two possibilities here:
2934 1) the playlist that the playlist source was based on
2935 is us, so just add the originals (which belonged to
2936 us anyway) back in the right place.
2938 2) the playlist that the playlist source was based on
2939 is NOT us, so we need to make copies of each of
2940 the original regions that we find, and add them
2943 bool same_playlist = (pls->original() == id());
2945 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
2947 boost::shared_ptr<Region> current (*i);
2949 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
2951 if (ca == cassocs.end()) {
2955 boost::shared_ptr<Region> original (ca->second);
2956 bool modified_region;
2958 if (i == rl.begin()) {
2959 move_offset = (target->position() - original->position()) - target->start();
2960 adjusted_start = original->position() + target->start();
2961 adjusted_end = adjusted_start + target->length();
2964 if (!same_playlist) {
2965 framepos_t pos = original->position();
2966 /* make a copy, but don't announce it */
2967 original = RegionFactory::create (original, false);
2968 /* the pure copy constructor resets position() to zero,
2971 original->set_position (pos);
2974 /* check to see how the original region (in the
2975 * playlist before compounding occured) overlaps
2976 * with the new state of the compound region.
2979 original->clear_changes ();
2980 modified_region = false;
2982 switch (original->coverage (adjusted_start, adjusted_end)) {
2983 case Evoral::OverlapNone:
2984 /* original region does not cover any part
2985 of the current state of the compound region
2989 case Evoral::OverlapInternal:
2990 /* overlap is just a small piece inside the
2991 * original so trim both ends
2993 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
2994 modified_region = true;
2997 case Evoral::OverlapExternal:
2998 /* overlap fully covers original, so leave it
3003 case Evoral::OverlapEnd:
3004 /* overlap starts within but covers end,
3005 so trim the front of the region
3007 original->trim_front (adjusted_start);
3008 modified_region = true;
3011 case Evoral::OverlapStart:
3012 /* overlap covers start but ends within, so
3013 * trim the end of the region.
3015 original->trim_end (adjusted_end);
3016 modified_region = true;
3021 /* fix the position to match any movement of the compound region.
3023 original->set_position (original->position() + move_offset);
3024 modified_region = true;
3027 if (modified_region) {
3028 _session.add_command (new StatefulDiffCommand (original));
3031 /* and add to the list of regions waiting to be
3035 originals.push_back (original);
3036 old_and_new_regions.push_back (TwoRegions (*i, original));
3039 pre_uncombine (originals, target);
3041 in_partition = true;
3044 // (3) remove the compound region
3046 remove_region (target);
3048 // (4) add the constituent regions
3050 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3051 add_region ((*i), (*i)->position());
3054 in_partition = false;
3059 Playlist::max_source_level () const
3061 RegionLock rlock (const_cast<Playlist *> (this));
3064 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3065 lvl = max (lvl, (*i)->max_source_level());
3072 Playlist::set_orig_track_id (const PBD::ID& id)
3074 _orig_track_id = id;
3077 /** Take a list of ranges, coalesce any that can be coalesced, then call
3078 * check_crossfades for each one.
3081 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3083 /* XXX: it's a shame that this coalesce algorithm also exists in
3084 TimeSelection::consolidate().
3087 /* XXX: xfade: this is implemented in Evoral::RangeList */
3090 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3091 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3097 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3098 i->from = min (i->from, j->from);
3099 i->to = max (i->to, j->to);
3106 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3107 check_crossfades (*i);