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.
25 #include <boost/lexical_cast.hpp>
27 #include "pbd/convert.h"
28 #include "pbd/stateful_diff_command.h"
29 #include "pbd/xml++.h"
31 #include "ardour/debug.h"
32 #include "ardour/playlist.h"
33 #include "ardour/session.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/region_sorters.h"
37 #include "ardour/playlist_factory.h"
38 #include "ardour/playlist_source.h"
39 #include "ardour/transient_detector.h"
40 #include "ardour/session_playlists.h"
41 #include "ardour/source_factory.h"
46 using namespace ARDOUR;
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> regions;
55 struct ShowMeTheList {
56 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
58 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
60 boost::shared_ptr<Playlist> playlist;
67 Playlist::make_property_quarks ()
69 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
70 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
71 Properties::regions.property_id));
74 RegionListProperty::RegionListProperty (Playlist& pl)
75 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
81 RegionListProperty::RegionListProperty (RegionListProperty const & p)
82 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
83 , _playlist (p._playlist)
89 RegionListProperty::clone () const
91 return new RegionListProperty (*this);
95 RegionListProperty::create () const
97 return new RegionListProperty (_playlist);
101 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
103 /* All regions (even those which are deleted) have their state saved by other
104 code, so we can just store ID here.
107 node.add_property ("id", region->id().to_s ());
110 boost::shared_ptr<Region>
111 RegionListProperty::get_content_from_xml (XMLNode const & node) const
113 XMLProperty const * prop = node.property ("id");
116 PBD::ID id (prop->value ());
118 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
121 ret = RegionFactory::region_by_id (id);
127 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
128 : SessionObject(sess, nom)
133 first_set_state = false;
138 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
139 : SessionObject(sess, "unnamed playlist")
144 const XMLProperty* prop = node.property("type");
145 assert(!prop || DataType(prop->value()) == _type);
149 _name = "unnamed"; /* reset by set_state */
152 /* set state called by derived class */
155 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
156 : SessionObject(other->_session, namestr)
158 , _type(other->_type)
159 , _orig_track_id (other->_orig_track_id)
164 other->copy_regions (tmp);
168 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
169 add_region_internal( (*x), (*x)->position());
174 _splicing = other->_splicing;
175 _nudging = other->_nudging;
176 _edit_mode = other->_edit_mode;
179 first_set_state = false;
181 in_partition = false;
183 _frozen = other->_frozen;
186 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
187 : SessionObject(other->_session, str)
189 , _type(other->_type)
190 , _orig_track_id (other->_orig_track_id)
192 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
194 framepos_t end = start + cnt - 1;
200 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
202 boost::shared_ptr<Region> region;
203 boost::shared_ptr<Region> new_region;
204 frameoffset_t offset = 0;
205 framepos_t position = 0;
208 Evoral::OverlapType overlap;
212 overlap = region->coverage (start, end);
215 case Evoral::OverlapNone:
218 case Evoral::OverlapInternal:
219 offset = start - region->position();
224 case Evoral::OverlapStart:
226 position = region->position() - start;
227 len = end - region->position();
230 case Evoral::OverlapEnd:
231 offset = start - region->position();
233 len = region->length() - offset;
236 case Evoral::OverlapExternal:
238 position = region->position() - start;
239 len = region->length();
243 RegionFactory::region_name (new_name, region->name(), false);
247 plist.add (Properties::start, region->start() + offset);
248 plist.add (Properties::length, len);
249 plist.add (Properties::name, new_name);
250 plist.add (Properties::layer, region->layer());
251 plist.add (Properties::layering_index, region->layering_index());
253 new_region = RegionFactory::create (region, plist);
255 add_region_internal (new_region, position);
259 first_set_state = false;
266 InUse (true); /* EMIT SIGNAL */
277 InUse (false); /* EMIT SIGNAL */
282 Playlist::copy_regions (RegionList& newlist) const
284 RegionReadLock rlock (const_cast<Playlist *> (this));
286 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
287 newlist.push_back (RegionFactory::create (*i, true));
292 Playlist::init (bool hide)
294 add_property (regions);
295 _xml_node_name = X_("Playlist");
297 g_atomic_int_set (&block_notifications, 0);
298 g_atomic_int_set (&ignore_state_changes, 0);
299 pending_contents_change = false;
300 pending_layering = false;
301 first_set_state = true;
309 _edit_mode = Config->get_edit_mode();
311 in_partition = false;
314 _capture_insertion_underway = false;
317 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
318 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
320 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
323 Playlist::~Playlist ()
325 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
328 RegionReadLock rl (this);
330 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
331 (*i)->set_playlist (boost::shared_ptr<Playlist>());
335 /* GoingAway must be emitted by derived classes */
339 Playlist::_set_sort_id ()
342 Playlists are given names like <track name>.<id>
343 or <track name>.<edit group name>.<id> where id
344 is an integer. We extract the id and sort by that.
347 size_t dot_position = _name.val().find_last_of(".");
349 if (dot_position == string::npos) {
352 string t = _name.val().substr(dot_position + 1);
355 _sort_id = boost::lexical_cast<int>(t);
358 catch (boost::bad_lexical_cast e) {
365 Playlist::set_name (const string& str)
367 /* in a typical situation, a playlist is being used
368 by one diskstream and also is referenced by the
369 Session. if there are more references than that,
370 then don't change the name.
377 bool ret = SessionObject::set_name(str);
384 /***********************************************************************
385 CHANGE NOTIFICATION HANDLING
387 Notifications must be delayed till the region_lock is released. This
388 is necessary because handlers for the signals may need to acquire
389 the lock (e.g. to read from the playlist).
390 ***********************************************************************/
393 Playlist::begin_undo ()
400 Playlist::end_undo ()
409 delay_notifications ();
410 g_atomic_int_inc (&ignore_state_changes);
413 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
415 Playlist::thaw (bool from_undo)
417 g_atomic_int_dec_and_test (&ignore_state_changes);
418 release_notifications (from_undo);
423 Playlist::delay_notifications ()
425 g_atomic_int_inc (&block_notifications);
428 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
430 Playlist::release_notifications (bool from_undo)
432 if (g_atomic_int_dec_and_test (&block_notifications)) {
433 flush_notifications (from_undo);
438 Playlist::notify_contents_changed ()
440 if (holding_state ()) {
441 pending_contents_change = true;
443 pending_contents_change = false;
444 ContentsChanged(); /* EMIT SIGNAL */
449 Playlist::notify_layering_changed ()
451 if (holding_state ()) {
452 pending_layering = true;
454 pending_layering = false;
455 LayeringChanged(); /* EMIT SIGNAL */
460 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
462 if (holding_state ()) {
463 pending_removes.insert (r);
464 pending_contents_change = true;
466 /* this might not be true, but we have to act
467 as though it could be.
469 pending_contents_change = false;
470 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
471 ContentsChanged (); /* EMIT SIGNAL */
476 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
478 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
480 if (holding_state ()) {
482 pending_range_moves.push_back (move);
486 list< Evoral::RangeMove<framepos_t> > m;
488 RangesMoved (m, false);
494 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
496 if (r->position() >= r->last_position()) {
497 /* trimmed shorter */
501 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
503 if (holding_state ()) {
505 pending_region_extensions.push_back (extra);
509 list<Evoral::Range<framepos_t> > r;
517 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
519 if (r->length() < r->last_length()) {
520 /* trimmed shorter */
523 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
525 if (holding_state ()) {
527 pending_region_extensions.push_back (extra);
531 list<Evoral::Range<framepos_t> > r;
539 Playlist::notify_region_added (boost::shared_ptr<Region> r)
541 /* the length change might not be true, but we have to act
542 as though it could be.
545 if (holding_state()) {
546 pending_adds.insert (r);
547 pending_contents_change = true;
550 pending_contents_change = false;
551 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
552 ContentsChanged (); /* EMIT SIGNAL */
556 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
558 Playlist::flush_notifications (bool from_undo)
560 set<boost::shared_ptr<Region> >::iterator s;
561 bool regions_changed = false;
569 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
570 regions_changed = true;
573 /* XXX: it'd be nice if we could use pending_bounds for
574 RegionsExtended and RegionsMoved.
577 /* we have no idea what order the regions ended up in pending
578 bounds (it could be based on selection order, for example).
579 so, to preserve layering in the "most recently moved is higher"
580 model, sort them by existing layer, then timestamp them.
583 // RegionSortByLayer cmp;
584 // pending_bounds.sort (cmp);
586 list<Evoral::Range<framepos_t> > crossfade_ranges;
588 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
589 crossfade_ranges.push_back ((*r)->last_range ());
590 crossfade_ranges.push_back ((*r)->range ());
593 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
594 crossfade_ranges.push_back ((*s)->range ());
595 remove_dependents (*s);
596 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
599 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
600 crossfade_ranges.push_back ((*s)->range ());
601 /* don't emit RegionAdded signal until relayering is done,
602 so that the region is fully setup by the time
603 anyone hears that its been added
607 if (((regions_changed || pending_contents_change) && !in_set_state) || pending_layering) {
611 if (regions_changed || pending_contents_change) {
612 pending_contents_change = false;
613 ContentsChanged (); /* EMIT SIGNAL */
616 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
617 (*s)->clear_changes ();
618 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
621 coalesce_and_check_crossfades (crossfade_ranges);
623 if (!pending_range_moves.empty ()) {
624 /* We don't need to check crossfades for these as pending_bounds has
627 RangesMoved (pending_range_moves, from_undo);
630 if (!pending_region_extensions.empty ()) {
631 RegionsExtended (pending_region_extensions);
640 Playlist::clear_pending ()
642 pending_adds.clear ();
643 pending_removes.clear ();
644 pending_bounds.clear ();
645 pending_range_moves.clear ();
646 pending_region_extensions.clear ();
647 pending_contents_change = false;
650 /*************************************************************
652 *************************************************************/
654 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
656 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
658 RegionWriteLock rlock (this);
659 times = fabs (times);
661 int itimes = (int) floor (times);
663 framepos_t pos = position;
665 if (times == 1 && auto_partition){
666 partition(pos - 1, (pos + region->length()), true);
670 add_region_internal (region, pos);
671 set_layer (region, DBL_MAX);
672 pos += region->length();
677 /* note that itimes can be zero if we being asked to just
678 insert a single fraction of the region.
681 for (int i = 0; i < itimes; ++i) {
682 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
683 add_region_internal (copy, pos);
684 set_layer (copy, DBL_MAX);
685 pos += region->length();
688 framecnt_t length = 0;
690 if (floor (times) != times) {
691 length = (framecnt_t) floor (region->length() * (times - floor (times)));
693 RegionFactory::region_name (name, region->name(), false);
698 plist.add (Properties::start, region->start());
699 plist.add (Properties::length, length);
700 plist.add (Properties::name, name);
701 plist.add (Properties::layer, region->layer());
703 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
704 add_region_internal (sub, pos);
705 set_layer (sub, DBL_MAX);
709 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
713 Playlist::set_region_ownership ()
715 RegionWriteLock rl (this);
716 RegionList::iterator i;
717 boost::weak_ptr<Playlist> pl (shared_from_this());
719 for (i = regions.begin(); i != regions.end(); ++i) {
720 (*i)->set_playlist (pl);
725 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
727 if (region->data_type() != _type) {
731 RegionSortByPosition cmp;
733 if (!first_set_state) {
734 boost::shared_ptr<Playlist> foo (shared_from_this());
735 region->set_playlist (boost::weak_ptr<Playlist>(foo));
738 region->set_position (position);
740 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
741 all_regions.insert (region);
743 possibly_splice_unlocked (position, region->length(), region);
745 if (!holding_state ()) {
746 /* layers get assigned from XML state, and are not reset during undo/redo */
750 /* we need to notify the existence of new region before checking dependents. Ick. */
752 notify_region_added (region);
754 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
760 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
762 RegionWriteLock rlock (this);
764 bool old_sp = _splicing;
767 remove_region_internal (old);
768 add_region_internal (newr, pos);
769 set_layer (newr, old->layer ());
773 possibly_splice_unlocked (pos, old->length() - newr->length());
777 Playlist::remove_region (boost::shared_ptr<Region> region)
779 RegionWriteLock rlock (this);
780 remove_region_internal (region);
784 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
786 RegionList::iterator i;
790 region->set_playlist (boost::weak_ptr<Playlist>());
793 /* XXX should probably freeze here .... */
795 for (i = regions.begin(); i != regions.end(); ++i) {
798 framepos_t pos = (*i)->position();
799 framecnt_t distance = (*i)->length();
803 possibly_splice_unlocked (pos, -distance);
805 if (!holding_state ()) {
807 remove_dependents (region);
810 notify_region_removed (region);
819 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
821 if (Config->get_use_overlap_equivalency()) {
822 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
823 if ((*i)->overlap_equivalent (other)) {
824 results.push_back (*i);
828 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
829 if ((*i)->equivalent (other)) {
830 results.push_back (*i);
837 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
839 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
841 if ((*i) && (*i)->region_list_equivalent (other)) {
842 results.push_back (*i);
848 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
850 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
852 if ((*i) && (*i)->any_source_equivalent (other)) {
853 results.push_back (*i);
859 Playlist::partition (framepos_t start, framepos_t end, bool cut)
863 partition_internal (start, end, cut, thawlist);
865 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
866 (*i)->resume_property_changes ();
870 /** Go through each region on the playlist and cut them at start and end, removing the section between
871 * start and end if cutting == true. Regions that lie entirely within start and end are always
876 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
878 RegionList new_regions;
881 RegionWriteLock rlock (this);
883 boost::shared_ptr<Region> region;
884 boost::shared_ptr<Region> current;
886 RegionList::iterator tmp;
887 Evoral::OverlapType overlap;
888 framepos_t pos1, pos2, pos3, pos4;
892 /* need to work from a copy, because otherwise the regions we add during the process
893 get operated on as well.
896 RegionList copy = regions.rlist();
898 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
905 if (current->first_frame() >= start && current->last_frame() < end) {
908 remove_region_internal (current);
914 /* coverage will return OverlapStart if the start coincides
915 with the end point. we do not partition such a region,
916 so catch this special case.
919 if (current->first_frame() >= end) {
923 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
927 pos1 = current->position();
930 pos4 = current->last_frame();
932 if (overlap == Evoral::OverlapInternal) {
933 /* split: we need 3 new regions, the front, middle and end.
934 cut: we need 2 regions, the front and end.
939 ---------------*************************------------
942 ---------------*****++++++++++++++++====------------
944 ---------------*****----------------====------------
949 /* "middle" ++++++ */
951 RegionFactory::region_name (new_name, current->name(), false);
955 plist.add (Properties::start, current->start() + (pos2 - pos1));
956 plist.add (Properties::length, pos3 - pos2);
957 plist.add (Properties::name, new_name);
958 plist.add (Properties::layer, current->layer ());
959 plist.add (Properties::layering_index, current->layering_index ());
960 plist.add (Properties::automatic, true);
961 plist.add (Properties::left_of_split, true);
962 plist.add (Properties::right_of_split, true);
964 region = RegionFactory::create (current, plist);
965 add_region_internal (region, start);
966 new_regions.push_back (region);
971 RegionFactory::region_name (new_name, current->name(), false);
975 plist.add (Properties::start, current->start() + (pos3 - pos1));
976 plist.add (Properties::length, pos4 - pos3);
977 plist.add (Properties::name, new_name);
978 plist.add (Properties::layer, current->layer ());
979 plist.add (Properties::layering_index, current->layering_index ());
980 plist.add (Properties::automatic, true);
981 plist.add (Properties::right_of_split, true);
983 region = RegionFactory::create (current, plist);
985 add_region_internal (region, end);
986 new_regions.push_back (region);
990 current->suspend_property_changes ();
991 thawlist.push_back (current);
992 current->cut_end (pos2 - 1);
994 } else if (overlap == Evoral::OverlapEnd) {
998 ---------------*************************------------
1001 ---------------**************+++++++++++------------
1003 ---------------**************-----------------------
1010 RegionFactory::region_name (new_name, current->name(), false);
1014 plist.add (Properties::start, current->start() + (pos2 - pos1));
1015 plist.add (Properties::length, pos4 - pos2);
1016 plist.add (Properties::name, new_name);
1017 plist.add (Properties::layer, current->layer ());
1018 plist.add (Properties::layering_index, current->layering_index ());
1019 plist.add (Properties::automatic, true);
1020 plist.add (Properties::left_of_split, true);
1022 region = RegionFactory::create (current, plist);
1024 add_region_internal (region, start);
1025 new_regions.push_back (region);
1030 current->suspend_property_changes ();
1031 thawlist.push_back (current);
1032 current->cut_end (pos2 - 1);
1034 } else if (overlap == Evoral::OverlapStart) {
1036 /* split: we need 2 regions: the front and the end.
1037 cut: just trim current to skip the cut area
1042 ---------------*************************------------
1046 ---------------****+++++++++++++++++++++------------
1048 -------------------*********************------------
1054 RegionFactory::region_name (new_name, current->name(), false);
1058 plist.add (Properties::start, current->start());
1059 plist.add (Properties::length, pos3 - pos1);
1060 plist.add (Properties::name, new_name);
1061 plist.add (Properties::layer, current->layer ());
1062 plist.add (Properties::layering_index, current->layering_index ());
1063 plist.add (Properties::automatic, true);
1064 plist.add (Properties::right_of_split, true);
1066 region = RegionFactory::create (current, plist);
1068 add_region_internal (region, pos1);
1069 new_regions.push_back (region);
1074 current->suspend_property_changes ();
1075 thawlist.push_back (current);
1076 current->trim_front (pos3);
1077 } else if (overlap == Evoral::OverlapExternal) {
1079 /* split: no split required.
1080 cut: remove the region.
1085 ---------------*************************------------
1089 ---------------*************************------------
1091 ----------------------------------------------------
1096 remove_region_internal (current);
1099 new_regions.push_back (current);
1103 in_partition = false;
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 RegionReadLock rl2 (other.get());
1201 int itimes = (int) floor (times);
1202 framepos_t pos = position;
1203 framecnt_t const shift = other->_get_extent().second;
1204 layer_t top = top_layer ();
1207 RegionWriteLock rl1 (this);
1209 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1210 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1212 /* put these new regions on top of all existing ones, but preserve
1213 the ordering they had in the original playlist.
1216 add_region_internal (copy_of_region, (*i)->position() + pos);
1217 set_layer (copy_of_region, copy_of_region->layer() + top);
1229 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1231 times = fabs (times);
1233 RegionWriteLock rl (this);
1234 int itimes = (int) floor (times);
1235 framepos_t pos = position + 1;
1238 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1239 add_region_internal (copy, pos);
1240 set_layer (copy, DBL_MAX);
1241 pos += region->length();
1244 if (floor (times) != times) {
1245 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1247 RegionFactory::region_name (name, region->name(), false);
1252 plist.add (Properties::start, region->start());
1253 plist.add (Properties::length, length);
1254 plist.add (Properties::name, name);
1256 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1257 add_region_internal (sub, pos);
1258 set_layer (sub, DBL_MAX);
1264 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1266 RegionWriteLock rlock (this);
1267 RegionList copy (regions.rlist());
1270 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1272 if ((*r)->last_frame() < at) {
1277 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1278 /* intersected region */
1279 if (!move_intersected) {
1284 /* do not move regions glued to music time - that
1285 has to be done separately.
1288 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1289 fixup.push_back (*r);
1293 (*r)->set_position ((*r)->position() + distance);
1296 /* XXX: may not be necessary; Region::post_set should do this, I think */
1297 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1298 (*r)->recompute_position_from_lock_style ();
1303 Playlist::split (framepos_t at)
1305 RegionWriteLock rlock (this);
1306 RegionList copy (regions.rlist());
1308 /* use a copy since this operation can modify the region list
1311 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1312 _split_region (*r, at);
1317 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1319 RegionWriteLock rl (this);
1320 _split_region (region, playlist_position);
1324 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1326 if (!region->covers (playlist_position)) {
1330 if (region->position() == playlist_position ||
1331 region->last_frame() == playlist_position) {
1335 boost::shared_ptr<Region> left;
1336 boost::shared_ptr<Region> right;
1337 frameoffset_t before;
1338 frameoffset_t after;
1342 /* split doesn't change anything about length, so don't try to splice */
1344 bool old_sp = _splicing;
1347 before = playlist_position - region->position();
1348 after = region->length() - before;
1350 RegionFactory::region_name (before_name, region->name(), false);
1355 plist.add (Properties::position, region->position ());
1356 plist.add (Properties::length, before);
1357 plist.add (Properties::name, before_name);
1358 plist.add (Properties::left_of_split, true);
1359 plist.add (Properties::layering_index, region->layering_index ());
1360 plist.add (Properties::layer, region->layer ());
1362 /* note: we must use the version of ::create with an offset here,
1363 since it supplies that offset to the Region constructor, which
1364 is necessary to get audio region gain envelopes right.
1366 left = RegionFactory::create (region, 0, plist);
1369 RegionFactory::region_name (after_name, region->name(), false);
1374 plist.add (Properties::position, region->position() + before);
1375 plist.add (Properties::length, after);
1376 plist.add (Properties::name, after_name);
1377 plist.add (Properties::right_of_split, true);
1378 plist.add (Properties::layering_index, region->layering_index ());
1379 plist.add (Properties::layer, region->layer ());
1381 /* same note as above */
1382 right = RegionFactory::create (region, before, plist);
1385 add_region_internal (left, region->position());
1386 add_region_internal (right, region->position() + before);
1387 remove_region_internal (region);
1393 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1395 if (_splicing || in_set_state) {
1396 /* don't respond to splicing moves or state setting */
1400 if (_edit_mode == Splice) {
1401 splice_locked (at, distance, exclude);
1406 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1408 if (_splicing || in_set_state) {
1409 /* don't respond to splicing moves or state setting */
1413 if (_edit_mode == Splice) {
1414 splice_unlocked (at, distance, exclude);
1419 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1422 RegionWriteLock rl (this);
1423 core_splice (at, distance, exclude);
1428 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1430 core_splice (at, distance, exclude);
1434 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1438 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1440 if (exclude && (*i) == exclude) {
1444 if ((*i)->position() >= at) {
1445 framepos_t new_pos = (*i)->position() + distance;
1448 } else if (new_pos >= max_framepos - (*i)->length()) {
1449 new_pos = max_framepos - (*i)->length();
1452 (*i)->set_position (new_pos);
1458 notify_contents_changed ();
1462 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1464 if (in_set_state || _splicing || _nudging || _shuffling) {
1468 if (what_changed.contains (Properties::position)) {
1470 /* remove it from the list then add it back in
1471 the right place again.
1474 RegionSortByPosition cmp;
1476 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1478 if (i == regions.end()) {
1479 /* the region bounds are being modified but its not currently
1480 in the region list. we will use its bounds correctly when/if
1487 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1490 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1492 frameoffset_t delta = 0;
1494 if (what_changed.contains (Properties::position)) {
1495 delta = region->position() - region->last_position();
1498 if (what_changed.contains (Properties::length)) {
1499 delta += region->length() - region->last_length();
1503 possibly_splice (region->last_position() + region->last_length(), delta, region);
1506 if (holding_state ()) {
1507 pending_bounds.push_back (region);
1509 notify_contents_changed ();
1511 list<Evoral::Range<framepos_t> > xf;
1512 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1513 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1514 coalesce_and_check_crossfades (xf);
1520 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1522 boost::shared_ptr<Region> region (weak_region.lock());
1528 /* this makes a virtual call to the right kind of playlist ... */
1530 region_changed (what_changed, region);
1534 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1536 PropertyChange our_interests;
1537 PropertyChange bounds;
1538 PropertyChange pos_and_length;
1541 if (in_set_state || in_flush) {
1545 our_interests.add (Properties::muted);
1546 our_interests.add (Properties::layer);
1547 our_interests.add (Properties::opaque);
1549 bounds.add (Properties::start);
1550 bounds.add (Properties::position);
1551 bounds.add (Properties::length);
1553 pos_and_length.add (Properties::position);
1554 pos_and_length.add (Properties::length);
1556 if (what_changed.contains (bounds)) {
1557 region_bounds_changed (what_changed, region);
1558 save = !(_splicing || _nudging);
1561 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1562 notify_region_moved (region);
1563 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1564 notify_region_end_trimmed (region);
1565 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1566 notify_region_start_trimmed (region);
1569 /* don't notify about layer changes, since we are the only object that can initiate
1570 them, and we notify in ::relayer()
1573 if (what_changed.contains (our_interests)) {
1581 Playlist::drop_regions ()
1583 RegionWriteLock rl (this);
1585 all_regions.clear ();
1589 Playlist::sync_all_regions_with_regions ()
1591 RegionWriteLock rl (this);
1593 all_regions.clear ();
1595 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1596 all_regions.insert (*i);
1601 Playlist::clear (bool with_signals)
1604 RegionWriteLock rl (this);
1606 region_state_changed_connections.drop_connections ();
1608 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1609 pending_removes.insert (*i);
1614 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1615 remove_dependents (*s);
1621 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1622 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1625 pending_removes.clear ();
1626 pending_contents_change = false;
1632 /***********************************************************************
1634 **********************************************************************/
1636 boost::shared_ptr<RegionList>
1637 Playlist::regions_at (framepos_t frame)
1639 RegionReadLock rlock (this);
1640 return find_regions_at (frame);
1644 Playlist::count_regions_at (framepos_t frame) const
1646 RegionReadLock rlock (const_cast<Playlist*>(this));
1649 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1650 if ((*i)->covers (frame)) {
1658 boost::shared_ptr<Region>
1659 Playlist::top_region_at (framepos_t frame)
1662 RegionReadLock rlock (this);
1663 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1664 boost::shared_ptr<Region> region;
1666 if (rlist->size()) {
1667 RegionSortByLayer cmp;
1669 region = rlist->back();
1675 boost::shared_ptr<Region>
1676 Playlist::top_unmuted_region_at (framepos_t frame)
1679 RegionReadLock rlock (this);
1680 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1682 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1684 RegionList::iterator tmp = i;
1687 if ((*i)->muted()) {
1694 boost::shared_ptr<Region> region;
1696 if (rlist->size()) {
1697 RegionSortByLayer cmp;
1699 region = rlist->back();
1705 boost::shared_ptr<RegionList>
1706 Playlist::find_regions_at (framepos_t frame)
1708 /* Caller must hold lock */
1710 boost::shared_ptr<RegionList> rlist (new RegionList);
1712 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1713 if ((*i)->covers (frame)) {
1714 rlist->push_back (*i);
1721 boost::shared_ptr<RegionList>
1722 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1724 RegionReadLock rlock (this);
1725 boost::shared_ptr<RegionList> rlist (new RegionList);
1727 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1728 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1729 rlist->push_back (*i);
1736 boost::shared_ptr<RegionList>
1737 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1739 RegionReadLock rlock (this);
1740 boost::shared_ptr<RegionList> rlist (new RegionList);
1742 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1743 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1744 rlist->push_back (*i);
1751 /** @param start Range start.
1752 * @param end Range end.
1753 * @return regions which have some part within this range.
1755 boost::shared_ptr<RegionList>
1756 Playlist::regions_touched (framepos_t start, framepos_t end)
1758 RegionReadLock rlock (this);
1759 return regions_touched_locked (start, end);
1762 boost::shared_ptr<RegionList>
1763 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1765 boost::shared_ptr<RegionList> rlist (new RegionList);
1767 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1768 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1769 rlist->push_back (*i);
1777 Playlist::find_next_transient (framepos_t from, int dir)
1779 RegionReadLock rlock (this);
1780 AnalysisFeatureList points;
1781 AnalysisFeatureList these_points;
1783 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1785 if ((*i)->last_frame() < from) {
1789 if ((*i)->first_frame() > from) {
1794 (*i)->get_transients (these_points);
1796 /* add first frame, just, err, because */
1798 these_points.push_back ((*i)->first_frame());
1800 points.insert (points.end(), these_points.begin(), these_points.end());
1801 these_points.clear ();
1804 if (points.empty()) {
1808 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1809 bool reached = false;
1812 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1817 if (reached && (*x) > from) {
1822 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1827 if (reached && (*x) < from) {
1836 boost::shared_ptr<Region>
1837 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1839 RegionReadLock rlock (this);
1840 boost::shared_ptr<Region> ret;
1841 framepos_t closest = max_framepos;
1843 bool end_iter = false;
1845 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1849 frameoffset_t distance;
1850 boost::shared_ptr<Region> r = (*i);
1855 pos = r->first_frame ();
1858 pos = r->last_frame ();
1861 pos = r->sync_position ();
1866 case 1: /* forwards */
1869 if ((distance = pos - frame) < closest) {
1878 default: /* backwards */
1881 if ((distance = frame - pos) < closest) {
1897 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1899 RegionReadLock rlock (this);
1901 framepos_t closest = max_framepos;
1902 framepos_t ret = -1;
1906 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1908 boost::shared_ptr<Region> r = (*i);
1909 frameoffset_t distance;
1911 if (r->first_frame() > frame) {
1913 distance = r->first_frame() - frame;
1915 if (distance < closest) {
1916 ret = r->first_frame();
1921 if (r->last_frame () > frame) {
1923 distance = r->last_frame () - frame;
1925 if (distance < closest) {
1926 ret = r->last_frame ();
1934 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1936 boost::shared_ptr<Region> r = (*i);
1937 frameoffset_t distance;
1939 if (r->last_frame() < frame) {
1941 distance = frame - r->last_frame();
1943 if (distance < closest) {
1944 ret = r->last_frame();
1949 if (r->first_frame() < frame) {
1951 distance = frame - r->first_frame();
1953 if (distance < closest) {
1954 ret = r->first_frame();
1965 /***********************************************************************/
1971 Playlist::mark_session_dirty ()
1973 if (!in_set_state && !holding_state ()) {
1974 _session.set_dirty();
1979 Playlist::rdiff (vector<Command*>& cmds) const
1981 RegionReadLock rlock (const_cast<Playlist *> (this));
1982 Stateful::rdiff (cmds);
1986 Playlist::clear_owned_changes ()
1988 RegionReadLock rlock (this);
1989 Stateful::clear_owned_changes ();
1993 Playlist::update (const RegionListProperty::ChangeRecord& change)
1995 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
1996 name(), change.added.size(), change.removed.size()));
1999 /* add the added regions */
2000 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2001 add_region_internal ((*i), (*i)->position());
2003 /* remove the removed regions */
2004 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2012 Playlist::set_state (const XMLNode& node, int version)
2016 XMLNodeConstIterator niter;
2017 XMLPropertyList plist;
2018 XMLPropertyConstIterator piter;
2020 boost::shared_ptr<Region> region;
2022 bool seen_region_nodes = false;
2027 if (node.name() != "Playlist") {
2034 plist = node.properties();
2038 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2042 if (prop->name() == X_("name")) {
2043 _name = prop->value();
2045 } else if (prop->name() == X_("orig-diskstream-id")) {
2046 /* XXX legacy session: fix up later */
2047 _orig_track_id = prop->value ();
2048 } else if (prop->name() == X_("orig-track-id")) {
2049 _orig_track_id = prop->value ();
2050 } else if (prop->name() == X_("frozen")) {
2051 _frozen = string_is_affirmative (prop->value());
2052 } else if (prop->name() == X_("combine-ops")) {
2053 _combine_ops = atoi (prop->value());
2059 nlist = node.children();
2061 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2065 if (child->name() == "Region") {
2067 seen_region_nodes = true;
2069 if ((prop = child->property ("id")) == 0) {
2070 error << _("region state node has no ID, ignored") << endmsg;
2074 ID id = prop->value ();
2076 if ((region = region_by_id (id))) {
2078 region->suspend_property_changes ();
2080 if (region->set_state (*child, version)) {
2081 region->resume_property_changes ();
2085 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2086 region->suspend_property_changes ();
2088 error << _("Playlist: cannot create region from XML") << endmsg;
2093 RegionWriteLock rlock (this);
2094 add_region_internal (region, region->position());
2097 region->resume_property_changes ();
2102 if (seen_region_nodes && regions.empty()) {
2107 notify_contents_changed ();
2110 first_set_state = false;
2116 Playlist::get_state()
2118 return state (true);
2122 Playlist::get_template()
2124 return state (false);
2127 /** @param full_state true to include regions in the returned state, otherwise false.
2130 Playlist::state (bool full_state)
2132 XMLNode *node = new XMLNode (X_("Playlist"));
2135 node->add_property (X_("id"), id().to_s());
2136 node->add_property (X_("name"), _name);
2137 node->add_property (X_("type"), _type.to_string());
2139 _orig_track_id.print (buf, sizeof (buf));
2140 node->add_property (X_("orig-track-id"), buf);
2141 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2144 RegionReadLock rlock (this);
2146 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2147 node->add_property ("combine-ops", buf);
2149 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2150 node->add_child_nocopy ((*i)->get_state());
2155 node->add_child_copy (*_extra_xml);
2162 Playlist::empty() const
2164 RegionReadLock rlock (const_cast<Playlist *>(this));
2165 return regions.empty();
2169 Playlist::n_regions() const
2171 RegionReadLock rlock (const_cast<Playlist *>(this));
2172 return regions.size();
2175 /** @return true if the all_regions list is empty, ie this playlist
2176 * has never had a region added to it.
2179 Playlist::all_regions_empty() const
2181 RegionReadLock rl (const_cast<Playlist *> (this));
2182 return all_regions.empty();
2185 pair<framepos_t, framepos_t>
2186 Playlist::get_extent () const
2188 RegionReadLock rlock (const_cast<Playlist *>(this));
2189 return _get_extent ();
2192 pair<framepos_t, framepos_t>
2193 Playlist::_get_extent () const
2195 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2197 if (regions.empty()) {
2202 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2203 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2204 if (e.first < ext.first) {
2205 ext.first = e.first;
2207 if (e.second > ext.second) {
2208 ext.second = e.second;
2216 Playlist::bump_name (string name, Session &session)
2218 string newname = name;
2221 newname = bump_name_once (newname, '.');
2222 } while (session.playlists->by_name (newname)!=NULL);
2229 Playlist::top_layer() const
2231 RegionReadLock rlock (const_cast<Playlist *> (this));
2234 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2235 top = max (top, (*i)->layer());
2241 Playlist::set_edit_mode (EditMode mode)
2246 struct RelayerSort {
2247 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2248 return a->layering_index() < b->layering_index();
2252 /** Set a new layer for a region. This adjusts the layering indices of all
2253 * regions in the playlist to put the specified region in the appropriate
2254 * place. The actual layering will be fixed up when relayer() happens.
2258 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2260 /* Remove the layer we are setting from our region list, and sort it */
2261 RegionList copy = regions.rlist();
2262 copy.remove (region);
2263 copy.sort (RelayerSort ());
2265 /* Put region back in the right place */
2266 RegionList::iterator i = copy.begin();
2267 while (i != copy.end ()) {
2268 if ((*i)->layer() > new_layer) {
2274 copy.insert (i, region);
2276 setup_layering_indices (copy);
2280 Playlist::setup_layering_indices (RegionList const & regions)
2283 list<Evoral::Range<framepos_t> > xf;
2285 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2286 (*k)->set_layering_index (j++);
2290 /** Take the layering indices of each of our regions, compute the layers
2291 * that they should be on, and write the layers back to the regions.
2294 Playlist::relayer ()
2296 /* never compute layers when setting from XML */
2302 /* Build up a new list of regions on each layer, stored in a set of lists
2303 each of which represent some period of time on some layer. The idea
2304 is to avoid having to search the entire region list to establish whether
2305 each region overlaps another */
2307 /* how many pieces to divide this playlist's time up into */
2308 int const divisions = 512;
2310 /* find the start and end positions of the regions on this playlist */
2311 framepos_t start = INT64_MAX;
2313 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2314 start = min (start, (*i)->position());
2315 end = max (end, (*i)->position() + (*i)->length());
2318 /* hence the size of each time division */
2319 double const division_size = (end - start) / double (divisions);
2321 vector<vector<RegionList> > layers;
2322 layers.push_back (vector<RegionList> (divisions));
2324 /* Sort our regions into layering index order */
2325 RegionList copy = regions.rlist();
2326 copy.sort (RelayerSort ());
2328 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2329 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2330 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2333 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2335 /* find the time divisions that this region covers; if there are no regions on the list,
2336 division_size will equal 0 and in this case we'll just say that
2337 start_division = end_division = 0.
2339 int start_division = 0;
2340 int end_division = 0;
2342 if (division_size > 0) {
2343 start_division = floor ( ((*i)->position() - start) / division_size);
2344 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2345 if (end_division == divisions) {
2350 assert (divisions == 0 || end_division < divisions);
2352 /* find the lowest layer that this region can go on */
2353 size_t j = layers.size();
2355 /* try layer j - 1; it can go on if it overlaps no other region
2356 that is already on that layer
2359 bool overlap = false;
2360 for (int k = start_division; k <= end_division; ++k) {
2361 RegionList::iterator l = layers[j-1][k].begin ();
2362 while (l != layers[j-1][k].end()) {
2363 if ((*l)->overlap_equivalent (*i)) {
2376 /* overlap, so we must use layer j */
2383 if (j == layers.size()) {
2384 /* we need a new layer for this region */
2385 layers.push_back (vector<RegionList> (divisions));
2388 /* put a reference to this region in each of the divisions that it exists in */
2389 for (int k = start_division; k <= end_division; ++k) {
2390 layers[j][k].push_back (*i);
2393 (*i)->set_layer (j);
2396 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2397 relayering because we just removed the only region on the top layer, nothing will
2398 appear to have changed, but the StreamView must still sort itself out. We could
2399 probably keep a note of the top layer last time we relayered, and check that,
2400 but premature optimisation &c...
2402 notify_layering_changed ();
2404 /* This relayer() may have been called as a result of a region removal, in which
2405 case we need to setup layering indices to account for the one that has just
2408 setup_layering_indices (copy);
2412 Playlist::raise_region (boost::shared_ptr<Region> region)
2414 set_layer (region, region->layer() + 1.5);
2419 Playlist::lower_region (boost::shared_ptr<Region> region)
2421 set_layer (region, region->layer() - 1.5);
2426 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2428 set_layer (region, DBL_MAX);
2433 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2435 set_layer (region, -0.5);
2440 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2442 RegionList::iterator i;
2448 RegionWriteLock 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 RegionReadLock 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 RegionReadLock 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 RegionReadLock 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 RegionWriteLock 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 RegionReadLock rlock (const_cast<Playlist*> (this));
2690 if (regions.size() > 1) {
2698 Playlist::update_after_tempo_map_change ()
2700 RegionWriteLock 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 RegionWriteLock 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 RegionReadLock (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 RegionWriteLock 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 RegionReadLock 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);
2828 copied_region->set_layer (original_region->layer ());
2830 /* use the maximum number of channels for any region */
2832 channels = max (channels, original_region->n_channels());
2834 /* it will go above the layer of the highest existing region */
2836 layer = max (layer, original_region->layer());
2839 pl->in_partition = false;
2841 pre_combine (copies);
2843 /* now create a new PlaylistSource for each channel in the new playlist */
2846 pair<framepos_t,framepos_t> extent = pl->get_extent();
2848 for (uint32_t chn = 0; chn < channels; ++chn) {
2849 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2853 /* now a new whole-file region using the list of sources */
2855 plist.add (Properties::start, 0);
2856 plist.add (Properties::length, extent.second);
2857 plist.add (Properties::name, parent_name);
2858 plist.add (Properties::whole_file, true);
2860 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2862 /* now the non-whole-file region that we will actually use in the
2867 plist.add (Properties::start, 0);
2868 plist.add (Properties::length, extent.second);
2869 plist.add (Properties::name, child_name);
2870 plist.add (Properties::layer, layer+1);
2872 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2874 /* remove all the selected regions from the current playlist
2879 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2883 /* do type-specific stuff with the originals and the new compound
2887 post_combine (originals, compound_region);
2889 /* add the new region at the right location */
2891 add_region (compound_region, earliest_position);
2897 return compound_region;
2901 Playlist::uncombine (boost::shared_ptr<Region> target)
2903 boost::shared_ptr<PlaylistSource> pls;
2904 boost::shared_ptr<const Playlist> pl;
2905 vector<boost::shared_ptr<Region> > originals;
2906 vector<TwoRegions> old_and_new_regions;
2908 // (1) check that its really a compound region
2910 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
2914 pl = pls->playlist();
2916 framepos_t adjusted_start = 0; // gcc isn't smart enough
2917 framepos_t adjusted_end = 0; // gcc isn't smart enough
2919 /* the leftmost (earliest) edge of the compound region
2920 starts at zero in its source, or larger if it
2921 has been trimmed or content-scrolled.
2923 the rightmost (latest) edge of the compound region
2924 relative to its source is the starting point plus
2925 the length of the region.
2928 // (2) get all the original regions
2930 const RegionList& rl (pl->region_list().rlist());
2931 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2932 frameoffset_t move_offset = 0;
2934 /* there are two possibilities here:
2935 1) the playlist that the playlist source was based on
2936 is us, so just add the originals (which belonged to
2937 us anyway) back in the right place.
2939 2) the playlist that the playlist source was based on
2940 is NOT us, so we need to make copies of each of
2941 the original regions that we find, and add them
2944 bool same_playlist = (pls->original() == id());
2946 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
2948 boost::shared_ptr<Region> current (*i);
2950 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
2952 if (ca == cassocs.end()) {
2956 boost::shared_ptr<Region> original (ca->second);
2957 bool modified_region;
2959 if (i == rl.begin()) {
2960 move_offset = (target->position() - original->position()) - target->start();
2961 adjusted_start = original->position() + target->start();
2962 adjusted_end = adjusted_start + target->length();
2965 if (!same_playlist) {
2966 framepos_t pos = original->position();
2967 /* make a copy, but don't announce it */
2968 original = RegionFactory::create (original, false);
2969 /* the pure copy constructor resets position() to zero,
2972 original->set_position (pos);
2975 /* check to see how the original region (in the
2976 * playlist before compounding occured) overlaps
2977 * with the new state of the compound region.
2980 original->clear_changes ();
2981 modified_region = false;
2983 switch (original->coverage (adjusted_start, adjusted_end)) {
2984 case Evoral::OverlapNone:
2985 /* original region does not cover any part
2986 of the current state of the compound region
2990 case Evoral::OverlapInternal:
2991 /* overlap is just a small piece inside the
2992 * original so trim both ends
2994 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
2995 modified_region = true;
2998 case Evoral::OverlapExternal:
2999 /* overlap fully covers original, so leave it
3004 case Evoral::OverlapEnd:
3005 /* overlap starts within but covers end,
3006 so trim the front of the region
3008 original->trim_front (adjusted_start);
3009 modified_region = true;
3012 case Evoral::OverlapStart:
3013 /* overlap covers start but ends within, so
3014 * trim the end of the region.
3016 original->trim_end (adjusted_end);
3017 modified_region = true;
3022 /* fix the position to match any movement of the compound region.
3024 original->set_position (original->position() + move_offset);
3025 modified_region = true;
3028 if (modified_region) {
3029 _session.add_command (new StatefulDiffCommand (original));
3032 /* and add to the list of regions waiting to be
3036 originals.push_back (original);
3037 old_and_new_regions.push_back (TwoRegions (*i, original));
3040 pre_uncombine (originals, target);
3042 in_partition = true;
3045 // (3) remove the compound region
3047 remove_region (target);
3049 // (4) add the constituent regions
3051 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3052 add_region ((*i), (*i)->position());
3055 in_partition = false;
3060 Playlist::max_source_level () const
3062 RegionReadLock rlock (const_cast<Playlist *> (this));
3065 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3066 lvl = max (lvl, (*i)->max_source_level());
3073 Playlist::set_orig_track_id (const PBD::ID& id)
3075 _orig_track_id = id;
3078 /** Take a list of ranges, coalesce any that can be coalesced, then call
3079 * check_crossfades for each one.
3082 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3084 /* XXX: it's a shame that this coalesce algorithm also exists in
3085 TimeSelection::consolidate().
3088 /* XXX: xfade: this is implemented in Evoral::RangeList */
3091 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3092 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3098 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3099 i->from = min (i->from, j->from);
3100 i->to = max (i->to, j->to);
3109 Playlist::set_capture_insertion_in_progress (bool yn)
3111 _capture_insertion_underway = yn;