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 _rippling = other->_rippling;
176 _nudging = other->_nudging;
177 _edit_mode = other->_edit_mode;
180 first_set_state = false;
182 in_partition = false;
184 _frozen = other->_frozen;
187 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
188 : SessionObject(other->_session, str)
190 , _type(other->_type)
191 , _orig_track_id (other->_orig_track_id)
193 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
195 framepos_t end = start + cnt - 1;
201 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
203 boost::shared_ptr<Region> region;
204 boost::shared_ptr<Region> new_region;
205 frameoffset_t offset = 0;
206 framepos_t position = 0;
209 Evoral::OverlapType overlap;
213 overlap = region->coverage (start, end);
216 case Evoral::OverlapNone:
219 case Evoral::OverlapInternal:
220 offset = start - region->position();
225 case Evoral::OverlapStart:
227 position = region->position() - start;
228 len = end - region->position();
231 case Evoral::OverlapEnd:
232 offset = start - region->position();
234 len = region->length() - offset;
237 case Evoral::OverlapExternal:
239 position = region->position() - start;
240 len = region->length();
244 RegionFactory::region_name (new_name, region->name(), false);
248 plist.add (Properties::start, region->start() + offset);
249 plist.add (Properties::length, len);
250 plist.add (Properties::name, new_name);
251 plist.add (Properties::layer, region->layer());
252 plist.add (Properties::layering_index, region->layering_index());
254 new_region = RegionFactory::create (region, plist);
256 add_region_internal (new_region, position);
260 first_set_state = false;
267 InUse (true); /* EMIT SIGNAL */
278 InUse (false); /* EMIT SIGNAL */
283 Playlist::copy_regions (RegionList& newlist) const
285 RegionReadLock rlock (const_cast<Playlist *> (this));
287 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
288 newlist.push_back (RegionFactory::create (*i, true));
293 Playlist::init (bool hide)
295 add_property (regions);
296 _xml_node_name = X_("Playlist");
298 g_atomic_int_set (&block_notifications, 0);
299 g_atomic_int_set (&ignore_state_changes, 0);
300 pending_contents_change = false;
301 pending_layering = false;
302 first_set_state = true;
311 _edit_mode = Config->get_edit_mode();
313 in_partition = false;
316 _capture_insertion_underway = false;
319 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
320 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
322 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
325 Playlist::~Playlist ()
327 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
330 RegionReadLock rl (this);
332 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
333 (*i)->set_playlist (boost::shared_ptr<Playlist>());
337 /* GoingAway must be emitted by derived classes */
341 Playlist::_set_sort_id ()
344 Playlists are given names like <track name>.<id>
345 or <track name>.<edit group name>.<id> where id
346 is an integer. We extract the id and sort by that.
349 size_t dot_position = _name.val().find_last_of(".");
351 if (dot_position == string::npos) {
354 string t = _name.val().substr(dot_position + 1);
357 _sort_id = boost::lexical_cast<int>(t);
360 catch (boost::bad_lexical_cast e) {
367 Playlist::set_name (const string& str)
369 /* in a typical situation, a playlist is being used
370 by one diskstream and also is referenced by the
371 Session. if there are more references than that,
372 then don't change the name.
379 bool ret = SessionObject::set_name(str);
386 /***********************************************************************
387 CHANGE NOTIFICATION HANDLING
389 Notifications must be delayed till the region_lock is released. This
390 is necessary because handlers for the signals may need to acquire
391 the lock (e.g. to read from the playlist).
392 ***********************************************************************/
395 Playlist::begin_undo ()
402 Playlist::end_undo ()
411 delay_notifications ();
412 g_atomic_int_inc (&ignore_state_changes);
415 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
417 Playlist::thaw (bool from_undo)
419 g_atomic_int_dec_and_test (&ignore_state_changes);
420 release_notifications (from_undo);
425 Playlist::delay_notifications ()
427 g_atomic_int_inc (&block_notifications);
430 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
432 Playlist::release_notifications (bool from_undo)
434 if (g_atomic_int_dec_and_test (&block_notifications)) {
435 flush_notifications (from_undo);
440 Playlist::notify_contents_changed ()
442 if (holding_state ()) {
443 pending_contents_change = true;
445 pending_contents_change = false;
446 ContentsChanged(); /* EMIT SIGNAL */
451 Playlist::notify_layering_changed ()
453 if (holding_state ()) {
454 pending_layering = true;
456 pending_layering = false;
457 LayeringChanged(); /* EMIT SIGNAL */
462 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
464 if (holding_state ()) {
465 pending_removes.insert (r);
466 pending_contents_change = true;
468 /* this might not be true, but we have to act
469 as though it could be.
471 pending_contents_change = false;
472 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
473 ContentsChanged (); /* EMIT SIGNAL */
478 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
480 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
482 if (holding_state ()) {
484 pending_range_moves.push_back (move);
488 list< Evoral::RangeMove<framepos_t> > m;
490 RangesMoved (m, false);
496 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
498 if (r->position() >= r->last_position()) {
499 /* trimmed shorter */
503 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
505 if (holding_state ()) {
507 pending_region_extensions.push_back (extra);
511 list<Evoral::Range<framepos_t> > r;
519 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
521 if (r->length() < r->last_length()) {
522 /* trimmed shorter */
525 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
527 if (holding_state ()) {
529 pending_region_extensions.push_back (extra);
533 list<Evoral::Range<framepos_t> > r;
541 Playlist::notify_region_added (boost::shared_ptr<Region> r)
543 /* the length change might not be true, but we have to act
544 as though it could be.
547 if (holding_state()) {
548 pending_adds.insert (r);
549 pending_contents_change = true;
552 pending_contents_change = false;
553 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
554 ContentsChanged (); /* EMIT SIGNAL */
558 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
560 Playlist::flush_notifications (bool from_undo)
562 set<boost::shared_ptr<Region> >::iterator s;
563 bool regions_changed = false;
571 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
572 regions_changed = true;
575 /* XXX: it'd be nice if we could use pending_bounds for
576 RegionsExtended and RegionsMoved.
579 /* we have no idea what order the regions ended up in pending
580 bounds (it could be based on selection order, for example).
581 so, to preserve layering in the "most recently moved is higher"
582 model, sort them by existing layer, then timestamp them.
585 // RegionSortByLayer cmp;
586 // pending_bounds.sort (cmp);
588 list<Evoral::Range<framepos_t> > crossfade_ranges;
590 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
591 crossfade_ranges.push_back ((*r)->last_range ());
592 crossfade_ranges.push_back ((*r)->range ());
595 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
596 crossfade_ranges.push_back ((*s)->range ());
597 remove_dependents (*s);
598 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
601 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
602 crossfade_ranges.push_back ((*s)->range ());
603 /* don't emit RegionAdded signal until relayering is done,
604 so that the region is fully setup by the time
605 anyone hears that its been added
609 if (((regions_changed || pending_contents_change) && !in_set_state) || pending_layering) {
613 if (regions_changed || pending_contents_change) {
614 pending_contents_change = false;
615 ContentsChanged (); /* EMIT SIGNAL */
618 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
619 (*s)->clear_changes ();
620 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
623 coalesce_and_check_crossfades (crossfade_ranges);
625 if (!pending_range_moves.empty ()) {
626 /* We don't need to check crossfades for these as pending_bounds has
629 RangesMoved (pending_range_moves, from_undo);
632 if (!pending_region_extensions.empty ()) {
633 RegionsExtended (pending_region_extensions);
642 Playlist::clear_pending ()
644 pending_adds.clear ();
645 pending_removes.clear ();
646 pending_bounds.clear ();
647 pending_range_moves.clear ();
648 pending_region_extensions.clear ();
649 pending_contents_change = false;
652 /*************************************************************
654 *************************************************************/
656 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
658 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
660 RegionWriteLock rlock (this);
661 times = fabs (times);
663 int itimes = (int) floor (times);
665 framepos_t pos = position;
667 if (times == 1 && auto_partition){
668 partition(pos - 1, (pos + region->length()), true);
672 add_region_internal (region, pos);
673 set_layer (region, DBL_MAX);
674 pos += region->length();
679 /* note that itimes can be zero if we being asked to just
680 insert a single fraction of the region.
683 for (int i = 0; i < itimes; ++i) {
684 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
685 add_region_internal (copy, pos);
686 set_layer (copy, DBL_MAX);
687 pos += region->length();
690 framecnt_t length = 0;
692 if (floor (times) != times) {
693 length = (framecnt_t) floor (region->length() * (times - floor (times)));
695 RegionFactory::region_name (name, region->name(), false);
700 plist.add (Properties::start, region->start());
701 plist.add (Properties::length, length);
702 plist.add (Properties::name, name);
703 plist.add (Properties::layer, region->layer());
705 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
706 add_region_internal (sub, pos);
707 set_layer (sub, DBL_MAX);
711 possibly_splice_unlocked (position, (pos + length) - position, region);
715 Playlist::set_region_ownership ()
717 RegionWriteLock rl (this);
718 RegionList::iterator i;
719 boost::weak_ptr<Playlist> pl (shared_from_this());
721 for (i = regions.begin(); i != regions.end(); ++i) {
722 (*i)->set_playlist (pl);
727 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
729 if (region->data_type() != _type) {
733 RegionSortByPosition cmp;
735 if (!first_set_state) {
736 boost::shared_ptr<Playlist> foo (shared_from_this());
737 region->set_playlist (boost::weak_ptr<Playlist>(foo));
740 region->set_position (position);
742 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
743 all_regions.insert (region);
745 possibly_splice_unlocked (position, region->length(), region);
747 if (!holding_state ()) {
748 /* layers get assigned from XML state, and are not reset during undo/redo */
752 /* we need to notify the existence of new region before checking dependents. Ick. */
754 notify_region_added (region);
756 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
762 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
764 RegionWriteLock rlock (this);
766 bool old_sp = _splicing;
769 remove_region_internal (old);
770 add_region_internal (newr, pos);
771 set_layer (newr, old->layer ());
775 possibly_splice_unlocked (pos, old->length() - newr->length());
779 Playlist::remove_region (boost::shared_ptr<Region> region)
781 RegionWriteLock rlock (this);
782 remove_region_internal (region);
786 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
788 RegionList::iterator i;
792 region->set_playlist (boost::weak_ptr<Playlist>());
795 /* XXX should probably freeze here .... */
797 for (i = regions.begin(); i != regions.end(); ++i) {
800 framepos_t pos = (*i)->position();
801 framecnt_t distance = (*i)->length();
805 possibly_splice_unlocked (pos, -distance);
807 if (!holding_state ()) {
809 remove_dependents (region);
812 notify_region_removed (region);
821 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
823 if (Config->get_use_overlap_equivalency()) {
824 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
825 if ((*i)->overlap_equivalent (other)) {
826 results.push_back (*i);
830 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
831 if ((*i)->equivalent (other)) {
832 results.push_back (*i);
839 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
841 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
843 if ((*i) && (*i)->region_list_equivalent (other)) {
844 results.push_back (*i);
850 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
852 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
854 if ((*i) && (*i)->any_source_equivalent (other)) {
855 results.push_back (*i);
861 Playlist::partition (framepos_t start, framepos_t end, bool cut)
865 partition_internal (start, end, cut, thawlist);
867 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
868 (*i)->resume_property_changes ();
872 /** Go through each region on the playlist and cut them at start and end, removing the section between
873 * start and end if cutting == true. Regions that lie entirely within start and end are always
878 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
880 RegionList new_regions;
883 RegionWriteLock rlock (this);
885 boost::shared_ptr<Region> region;
886 boost::shared_ptr<Region> current;
888 RegionList::iterator tmp;
889 Evoral::OverlapType overlap;
890 framepos_t pos1, pos2, pos3, pos4;
894 /* need to work from a copy, because otherwise the regions we add during the process
895 get operated on as well.
898 RegionList copy = regions.rlist();
900 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
907 if (current->first_frame() >= start && current->last_frame() < end) {
910 remove_region_internal (current);
916 /* coverage will return OverlapStart if the start coincides
917 with the end point. we do not partition such a region,
918 so catch this special case.
921 if (current->first_frame() >= end) {
925 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
929 pos1 = current->position();
932 pos4 = current->last_frame();
934 if (overlap == Evoral::OverlapInternal) {
935 /* split: we need 3 new regions, the front, middle and end.
936 cut: we need 2 regions, the front and end.
941 ---------------*************************------------
944 ---------------*****++++++++++++++++====------------
946 ---------------*****----------------====------------
951 /* "middle" ++++++ */
953 RegionFactory::region_name (new_name, current->name(), false);
957 plist.add (Properties::start, current->start() + (pos2 - pos1));
958 plist.add (Properties::length, pos3 - pos2);
959 plist.add (Properties::name, new_name);
960 plist.add (Properties::layer, current->layer ());
961 plist.add (Properties::layering_index, current->layering_index ());
962 plist.add (Properties::automatic, true);
963 plist.add (Properties::left_of_split, true);
964 plist.add (Properties::right_of_split, true);
966 region = RegionFactory::create (current, plist);
967 add_region_internal (region, start);
968 new_regions.push_back (region);
973 RegionFactory::region_name (new_name, current->name(), false);
977 plist.add (Properties::start, current->start() + (pos3 - pos1));
978 plist.add (Properties::length, pos4 - pos3);
979 plist.add (Properties::name, new_name);
980 plist.add (Properties::layer, current->layer ());
981 plist.add (Properties::layering_index, current->layering_index ());
982 plist.add (Properties::automatic, true);
983 plist.add (Properties::right_of_split, true);
985 region = RegionFactory::create (current, plist);
987 add_region_internal (region, end);
988 new_regions.push_back (region);
992 current->suspend_property_changes ();
993 thawlist.push_back (current);
994 current->cut_end (pos2 - 1);
996 } else if (overlap == Evoral::OverlapEnd) {
1000 ---------------*************************------------
1003 ---------------**************+++++++++++------------
1005 ---------------**************-----------------------
1012 RegionFactory::region_name (new_name, current->name(), false);
1016 plist.add (Properties::start, current->start() + (pos2 - pos1));
1017 plist.add (Properties::length, pos4 - pos2);
1018 plist.add (Properties::name, new_name);
1019 plist.add (Properties::layer, current->layer ());
1020 plist.add (Properties::layering_index, current->layering_index ());
1021 plist.add (Properties::automatic, true);
1022 plist.add (Properties::left_of_split, true);
1024 region = RegionFactory::create (current, plist);
1026 add_region_internal (region, start);
1027 new_regions.push_back (region);
1032 current->suspend_property_changes ();
1033 thawlist.push_back (current);
1034 current->cut_end (pos2 - 1);
1036 } else if (overlap == Evoral::OverlapStart) {
1038 /* split: we need 2 regions: the front and the end.
1039 cut: just trim current to skip the cut area
1044 ---------------*************************------------
1048 ---------------****+++++++++++++++++++++------------
1050 -------------------*********************------------
1056 RegionFactory::region_name (new_name, current->name(), false);
1060 plist.add (Properties::start, current->start());
1061 plist.add (Properties::length, pos3 - pos1);
1062 plist.add (Properties::name, new_name);
1063 plist.add (Properties::layer, current->layer ());
1064 plist.add (Properties::layering_index, current->layering_index ());
1065 plist.add (Properties::automatic, true);
1066 plist.add (Properties::right_of_split, true);
1068 region = RegionFactory::create (current, plist);
1070 add_region_internal (region, pos1);
1071 new_regions.push_back (region);
1076 current->suspend_property_changes ();
1077 thawlist.push_back (current);
1078 current->trim_front (pos3);
1079 } else if (overlap == Evoral::OverlapExternal) {
1081 /* split: no split required.
1082 cut: remove the region.
1087 ---------------*************************------------
1091 ---------------*************************------------
1093 ----------------------------------------------------
1098 remove_region_internal (current);
1101 new_regions.push_back (current);
1105 in_partition = false;
1109 boost::shared_ptr<Playlist>
1110 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1112 boost::shared_ptr<Playlist> ret;
1113 boost::shared_ptr<Playlist> pl;
1116 if (ranges.empty()) {
1117 return boost::shared_ptr<Playlist>();
1120 start = ranges.front().start;
1122 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1124 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1126 if (i == ranges.begin()) {
1130 /* paste the next section into the nascent playlist,
1131 offset to reflect the start of the first range we
1135 ret->paste (pl, (*i).start - start, 1.0f);
1142 boost::shared_ptr<Playlist>
1143 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1145 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1146 return cut_copy (pmf, ranges, result_is_hidden);
1149 boost::shared_ptr<Playlist>
1150 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1152 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1153 return cut_copy (pmf, ranges, result_is_hidden);
1156 boost::shared_ptr<Playlist>
1157 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1159 boost::shared_ptr<Playlist> the_copy;
1160 RegionList thawlist;
1163 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1164 string new_name = _name;
1168 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1169 return boost::shared_ptr<Playlist>();
1172 partition_internal (start, start+cnt-1, true, thawlist);
1174 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1175 (*i)->resume_property_changes();
1181 boost::shared_ptr<Playlist>
1182 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1186 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1187 string new_name = _name;
1191 cnt = min (_get_extent().second - start, cnt);
1192 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1196 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1198 times = fabs (times);
1201 RegionReadLock rl2 (other.get());
1203 int itimes = (int) floor (times);
1204 framepos_t pos = position;
1205 framecnt_t const shift = other->_get_extent().second;
1206 layer_t top = top_layer ();
1209 RegionWriteLock rl1 (this);
1211 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1212 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1214 /* put these new regions on top of all existing ones, but preserve
1215 the ordering they had in the original playlist.
1218 add_region_internal (copy_of_region, (*i)->position() + pos);
1219 set_layer (copy_of_region, copy_of_region->layer() + top);
1231 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1233 times = fabs (times);
1235 RegionWriteLock rl (this);
1236 int itimes = (int) floor (times);
1237 framepos_t pos = position + 1;
1240 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1241 add_region_internal (copy, pos);
1242 set_layer (copy, DBL_MAX);
1243 pos += region->length();
1246 if (floor (times) != times) {
1247 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1249 RegionFactory::region_name (name, region->name(), false);
1254 plist.add (Properties::start, region->start());
1255 plist.add (Properties::length, length);
1256 plist.add (Properties::name, name);
1258 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1259 add_region_internal (sub, pos);
1260 set_layer (sub, DBL_MAX);
1266 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1268 RegionWriteLock rlock (this);
1269 RegionList copy (regions.rlist());
1272 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1274 if ((*r)->last_frame() < at) {
1279 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1280 /* intersected region */
1281 if (!move_intersected) {
1286 /* do not move regions glued to music time - that
1287 has to be done separately.
1290 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1291 fixup.push_back (*r);
1295 (*r)->set_position ((*r)->position() + distance);
1298 /* XXX: may not be necessary; Region::post_set should do this, I think */
1299 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1300 (*r)->recompute_position_from_lock_style ();
1305 Playlist::split (framepos_t at)
1307 RegionWriteLock rlock (this);
1308 RegionList copy (regions.rlist());
1310 /* use a copy since this operation can modify the region list
1313 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1314 _split_region (*r, at);
1319 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1321 RegionWriteLock rl (this);
1322 _split_region (region, playlist_position);
1326 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1328 if (!region->covers (playlist_position)) {
1332 if (region->position() == playlist_position ||
1333 region->last_frame() == playlist_position) {
1337 boost::shared_ptr<Region> left;
1338 boost::shared_ptr<Region> right;
1339 frameoffset_t before;
1340 frameoffset_t after;
1344 /* split doesn't change anything about length, so don't try to splice */
1346 bool old_sp = _splicing;
1349 before = playlist_position - region->position();
1350 after = region->length() - before;
1352 RegionFactory::region_name (before_name, region->name(), false);
1357 plist.add (Properties::position, region->position ());
1358 plist.add (Properties::length, before);
1359 plist.add (Properties::name, before_name);
1360 plist.add (Properties::left_of_split, true);
1361 plist.add (Properties::layering_index, region->layering_index ());
1362 plist.add (Properties::layer, region->layer ());
1364 /* note: we must use the version of ::create with an offset here,
1365 since it supplies that offset to the Region constructor, which
1366 is necessary to get audio region gain envelopes right.
1368 left = RegionFactory::create (region, 0, plist);
1371 RegionFactory::region_name (after_name, region->name(), false);
1376 plist.add (Properties::position, region->position() + before);
1377 plist.add (Properties::length, after);
1378 plist.add (Properties::name, after_name);
1379 plist.add (Properties::right_of_split, true);
1380 plist.add (Properties::layering_index, region->layering_index ());
1381 plist.add (Properties::layer, region->layer ());
1383 /* same note as above */
1384 right = RegionFactory::create (region, before, plist);
1387 add_region_internal (left, region->position());
1388 add_region_internal (right, region->position() + before);
1389 remove_region_internal (region);
1395 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1397 if (_splicing || in_set_state) {
1398 /* don't respond to splicing moves or state setting */
1402 if (_edit_mode == Splice) {
1403 splice_locked (at, distance, exclude);
1408 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1410 if (_splicing || in_set_state) {
1411 /* don't respond to splicing moves or state setting */
1415 if (_edit_mode == Splice) {
1416 splice_unlocked (at, distance, exclude);
1421 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1424 RegionWriteLock rl (this);
1425 core_splice (at, distance, exclude);
1430 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1432 core_splice (at, distance, exclude);
1436 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1440 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1442 if (exclude && (*i) == exclude) {
1446 if ((*i)->position() >= at) {
1447 framepos_t new_pos = (*i)->position() + distance;
1450 } else if (new_pos >= max_framepos - (*i)->length()) {
1451 new_pos = max_framepos - (*i)->length();
1454 (*i)->set_position (new_pos);
1460 notify_contents_changed ();
1464 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1467 RegionWriteLock rl (this);
1468 core_ripple (at, distance, exclude);
1473 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1475 core_ripple (at, distance, exclude);
1479 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1481 if (distance == 0) {
1486 RegionListProperty copy = regions;
1487 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1488 assert (i != copy.end());
1491 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1496 if ((*i)->position() >= at) {
1497 framepos_t new_pos = (*i)->position() + distance;
1498 framepos_t limit = max_framepos - (*i)->length();
1501 } else if (new_pos >= limit ) {
1505 (*i)->set_position (new_pos);
1510 notify_contents_changed ();
1515 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1517 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1521 if (what_changed.contains (Properties::position)) {
1523 /* remove it from the list then add it back in
1524 the right place again.
1527 RegionSortByPosition cmp;
1529 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1531 if (i == regions.end()) {
1532 /* the region bounds are being modified but its not currently
1533 in the region list. we will use its bounds correctly when/if
1540 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1543 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1545 frameoffset_t delta = 0;
1547 if (what_changed.contains (Properties::position)) {
1548 delta = region->position() - region->last_position();
1551 if (what_changed.contains (Properties::length)) {
1552 delta += region->length() - region->last_length();
1556 possibly_splice (region->last_position() + region->last_length(), delta, region);
1559 if (holding_state ()) {
1560 pending_bounds.push_back (region);
1562 notify_contents_changed ();
1564 list<Evoral::Range<framepos_t> > xf;
1565 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1566 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1567 coalesce_and_check_crossfades (xf);
1573 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1575 boost::shared_ptr<Region> region (weak_region.lock());
1581 /* this makes a virtual call to the right kind of playlist ... */
1583 region_changed (what_changed, region);
1587 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1589 PropertyChange our_interests;
1590 PropertyChange bounds;
1591 PropertyChange pos_and_length;
1594 if (in_set_state || in_flush) {
1598 our_interests.add (Properties::muted);
1599 our_interests.add (Properties::layer);
1600 our_interests.add (Properties::opaque);
1602 bounds.add (Properties::start);
1603 bounds.add (Properties::position);
1604 bounds.add (Properties::length);
1606 pos_and_length.add (Properties::position);
1607 pos_and_length.add (Properties::length);
1609 if (what_changed.contains (bounds)) {
1610 region_bounds_changed (what_changed, region);
1611 save = !(_splicing || _nudging);
1614 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1615 notify_region_moved (region);
1616 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1617 notify_region_end_trimmed (region);
1618 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1619 notify_region_start_trimmed (region);
1622 /* don't notify about layer changes, since we are the only object that can initiate
1623 them, and we notify in ::relayer()
1626 if (what_changed.contains (our_interests)) {
1634 Playlist::drop_regions ()
1636 RegionWriteLock rl (this);
1638 all_regions.clear ();
1642 Playlist::sync_all_regions_with_regions ()
1644 RegionWriteLock rl (this);
1646 all_regions.clear ();
1648 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1649 all_regions.insert (*i);
1654 Playlist::clear (bool with_signals)
1657 RegionWriteLock rl (this);
1659 region_state_changed_connections.drop_connections ();
1661 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1662 pending_removes.insert (*i);
1667 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1668 remove_dependents (*s);
1674 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1675 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1678 pending_removes.clear ();
1679 pending_contents_change = false;
1685 /***********************************************************************
1687 **********************************************************************/
1689 boost::shared_ptr<RegionList>
1690 Playlist::regions_at (framepos_t frame)
1692 RegionReadLock rlock (this);
1693 return find_regions_at (frame);
1697 Playlist::count_regions_at (framepos_t frame) const
1699 RegionReadLock rlock (const_cast<Playlist*>(this));
1702 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1703 if ((*i)->covers (frame)) {
1711 boost::shared_ptr<Region>
1712 Playlist::top_region_at (framepos_t frame)
1715 RegionReadLock rlock (this);
1716 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1717 boost::shared_ptr<Region> region;
1719 if (rlist->size()) {
1720 RegionSortByLayer cmp;
1722 region = rlist->back();
1728 boost::shared_ptr<Region>
1729 Playlist::top_unmuted_region_at (framepos_t frame)
1732 RegionReadLock rlock (this);
1733 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1735 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1737 RegionList::iterator tmp = i;
1740 if ((*i)->muted()) {
1747 boost::shared_ptr<Region> region;
1749 if (rlist->size()) {
1750 RegionSortByLayer cmp;
1752 region = rlist->back();
1758 boost::shared_ptr<RegionList>
1759 Playlist::find_regions_at (framepos_t frame)
1761 /* Caller must hold lock */
1763 boost::shared_ptr<RegionList> rlist (new RegionList);
1765 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1766 if ((*i)->covers (frame)) {
1767 rlist->push_back (*i);
1774 boost::shared_ptr<RegionList>
1775 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1777 RegionReadLock rlock (this);
1778 boost::shared_ptr<RegionList> rlist (new RegionList);
1780 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1781 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1782 rlist->push_back (*i);
1789 boost::shared_ptr<RegionList>
1790 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1792 RegionReadLock rlock (this);
1793 boost::shared_ptr<RegionList> rlist (new RegionList);
1795 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1796 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1797 rlist->push_back (*i);
1804 /** @param start Range start.
1805 * @param end Range end.
1806 * @return regions which have some part within this range.
1808 boost::shared_ptr<RegionList>
1809 Playlist::regions_touched (framepos_t start, framepos_t end)
1811 RegionReadLock rlock (this);
1812 return regions_touched_locked (start, end);
1815 boost::shared_ptr<RegionList>
1816 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1818 boost::shared_ptr<RegionList> rlist (new RegionList);
1820 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1821 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1822 rlist->push_back (*i);
1830 Playlist::find_next_transient (framepos_t from, int dir)
1832 RegionReadLock rlock (this);
1833 AnalysisFeatureList points;
1834 AnalysisFeatureList these_points;
1836 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1838 if ((*i)->last_frame() < from) {
1842 if ((*i)->first_frame() > from) {
1847 (*i)->get_transients (these_points);
1849 /* add first frame, just, err, because */
1851 these_points.push_back ((*i)->first_frame());
1853 points.insert (points.end(), these_points.begin(), these_points.end());
1854 these_points.clear ();
1857 if (points.empty()) {
1861 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1862 bool reached = false;
1865 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1870 if (reached && (*x) > from) {
1875 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1880 if (reached && (*x) < from) {
1889 boost::shared_ptr<Region>
1890 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1892 RegionReadLock rlock (this);
1893 boost::shared_ptr<Region> ret;
1894 framepos_t closest = max_framepos;
1896 bool end_iter = false;
1898 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1902 frameoffset_t distance;
1903 boost::shared_ptr<Region> r = (*i);
1908 pos = r->first_frame ();
1911 pos = r->last_frame ();
1914 pos = r->sync_position ();
1919 case 1: /* forwards */
1922 if ((distance = pos - frame) < closest) {
1931 default: /* backwards */
1934 if ((distance = frame - pos) < closest) {
1950 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1952 RegionReadLock rlock (this);
1954 framepos_t closest = max_framepos;
1955 framepos_t ret = -1;
1959 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1961 boost::shared_ptr<Region> r = (*i);
1962 frameoffset_t distance;
1964 if (r->first_frame() > frame) {
1966 distance = r->first_frame() - frame;
1968 if (distance < closest) {
1969 ret = r->first_frame();
1974 if (r->last_frame () > frame) {
1976 distance = r->last_frame () - frame;
1978 if (distance < closest) {
1979 ret = r->last_frame ();
1987 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1989 boost::shared_ptr<Region> r = (*i);
1990 frameoffset_t distance;
1992 if (r->last_frame() < frame) {
1994 distance = frame - r->last_frame();
1996 if (distance < closest) {
1997 ret = r->last_frame();
2002 if (r->first_frame() < frame) {
2004 distance = frame - r->first_frame();
2006 if (distance < closest) {
2007 ret = r->first_frame();
2018 /***********************************************************************/
2024 Playlist::mark_session_dirty ()
2026 if (!in_set_state && !holding_state ()) {
2027 _session.set_dirty();
2032 Playlist::rdiff (vector<Command*>& cmds) const
2034 RegionReadLock rlock (const_cast<Playlist *> (this));
2035 Stateful::rdiff (cmds);
2039 Playlist::clear_owned_changes ()
2041 RegionReadLock rlock (this);
2042 Stateful::clear_owned_changes ();
2046 Playlist::update (const RegionListProperty::ChangeRecord& change)
2048 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2049 name(), change.added.size(), change.removed.size()));
2052 /* add the added regions */
2053 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2054 add_region_internal ((*i), (*i)->position());
2056 /* remove the removed regions */
2057 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2065 Playlist::set_state (const XMLNode& node, int version)
2069 XMLNodeConstIterator niter;
2070 XMLPropertyList plist;
2071 XMLPropertyConstIterator piter;
2073 boost::shared_ptr<Region> region;
2075 bool seen_region_nodes = false;
2080 if (node.name() != "Playlist") {
2087 plist = node.properties();
2091 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2095 if (prop->name() == X_("name")) {
2096 _name = prop->value();
2098 } else if (prop->name() == X_("orig-diskstream-id")) {
2099 /* XXX legacy session: fix up later */
2100 _orig_track_id = prop->value ();
2101 } else if (prop->name() == X_("orig-track-id")) {
2102 _orig_track_id = prop->value ();
2103 } else if (prop->name() == X_("frozen")) {
2104 _frozen = string_is_affirmative (prop->value());
2105 } else if (prop->name() == X_("combine-ops")) {
2106 _combine_ops = atoi (prop->value());
2112 nlist = node.children();
2114 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2118 if (child->name() == "Region") {
2120 seen_region_nodes = true;
2122 if ((prop = child->property ("id")) == 0) {
2123 error << _("region state node has no ID, ignored") << endmsg;
2127 ID id = prop->value ();
2129 if ((region = region_by_id (id))) {
2131 region->suspend_property_changes ();
2133 if (region->set_state (*child, version)) {
2134 region->resume_property_changes ();
2138 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2139 region->suspend_property_changes ();
2141 error << _("Playlist: cannot create region from XML") << endmsg;
2146 RegionWriteLock rlock (this);
2147 add_region_internal (region, region->position());
2150 region->resume_property_changes ();
2155 if (seen_region_nodes && regions.empty()) {
2160 notify_contents_changed ();
2163 first_set_state = false;
2169 Playlist::get_state()
2171 return state (true);
2175 Playlist::get_template()
2177 return state (false);
2180 /** @param full_state true to include regions in the returned state, otherwise false.
2183 Playlist::state (bool full_state)
2185 XMLNode *node = new XMLNode (X_("Playlist"));
2188 node->add_property (X_("id"), id().to_s());
2189 node->add_property (X_("name"), _name);
2190 node->add_property (X_("type"), _type.to_string());
2192 _orig_track_id.print (buf, sizeof (buf));
2193 node->add_property (X_("orig-track-id"), buf);
2194 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2197 RegionReadLock rlock (this);
2199 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2200 node->add_property ("combine-ops", buf);
2202 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2203 node->add_child_nocopy ((*i)->get_state());
2208 node->add_child_copy (*_extra_xml);
2215 Playlist::empty() const
2217 RegionReadLock rlock (const_cast<Playlist *>(this));
2218 return regions.empty();
2222 Playlist::n_regions() const
2224 RegionReadLock rlock (const_cast<Playlist *>(this));
2225 return regions.size();
2228 /** @return true if the all_regions list is empty, ie this playlist
2229 * has never had a region added to it.
2232 Playlist::all_regions_empty() const
2234 RegionReadLock rl (const_cast<Playlist *> (this));
2235 return all_regions.empty();
2238 pair<framepos_t, framepos_t>
2239 Playlist::get_extent () const
2241 RegionReadLock rlock (const_cast<Playlist *>(this));
2242 return _get_extent ();
2245 pair<framepos_t, framepos_t>
2246 Playlist::_get_extent () const
2248 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2250 if (regions.empty()) {
2255 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2256 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2257 if (e.first < ext.first) {
2258 ext.first = e.first;
2260 if (e.second > ext.second) {
2261 ext.second = e.second;
2269 Playlist::bump_name (string name, Session &session)
2271 string newname = name;
2274 newname = bump_name_once (newname, '.');
2275 } while (session.playlists->by_name (newname)!=NULL);
2282 Playlist::top_layer() const
2284 RegionReadLock rlock (const_cast<Playlist *> (this));
2287 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2288 top = max (top, (*i)->layer());
2294 Playlist::set_edit_mode (EditMode mode)
2299 struct RelayerSort {
2300 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2301 return a->layering_index() < b->layering_index();
2305 /** Set a new layer for a region. This adjusts the layering indices of all
2306 * regions in the playlist to put the specified region in the appropriate
2307 * place. The actual layering will be fixed up when relayer() happens.
2311 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2313 /* Remove the layer we are setting from our region list, and sort it */
2314 RegionList copy = regions.rlist();
2315 copy.remove (region);
2316 copy.sort (RelayerSort ());
2318 /* Put region back in the right place */
2319 RegionList::iterator i = copy.begin();
2320 while (i != copy.end ()) {
2321 if ((*i)->layer() > new_layer) {
2327 copy.insert (i, region);
2329 setup_layering_indices (copy);
2333 Playlist::setup_layering_indices (RegionList const & regions)
2336 list<Evoral::Range<framepos_t> > xf;
2338 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2339 (*k)->set_layering_index (j++);
2343 /** Take the layering indices of each of our regions, compute the layers
2344 * that they should be on, and write the layers back to the regions.
2347 Playlist::relayer ()
2349 /* never compute layers when setting from XML */
2355 /* Build up a new list of regions on each layer, stored in a set of lists
2356 each of which represent some period of time on some layer. The idea
2357 is to avoid having to search the entire region list to establish whether
2358 each region overlaps another */
2360 /* how many pieces to divide this playlist's time up into */
2361 int const divisions = 512;
2363 /* find the start and end positions of the regions on this playlist */
2364 framepos_t start = INT64_MAX;
2366 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2367 start = min (start, (*i)->position());
2368 end = max (end, (*i)->position() + (*i)->length());
2371 /* hence the size of each time division */
2372 double const division_size = (end - start) / double (divisions);
2374 vector<vector<RegionList> > layers;
2375 layers.push_back (vector<RegionList> (divisions));
2377 /* Sort our regions into layering index order */
2378 RegionList copy = regions.rlist();
2379 copy.sort (RelayerSort ());
2381 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2382 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2383 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2386 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2388 /* find the time divisions that this region covers; if there are no regions on the list,
2389 division_size will equal 0 and in this case we'll just say that
2390 start_division = end_division = 0.
2392 int start_division = 0;
2393 int end_division = 0;
2395 if (division_size > 0) {
2396 start_division = floor ( ((*i)->position() - start) / division_size);
2397 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2398 if (end_division == divisions) {
2403 assert (divisions == 0 || end_division < divisions);
2405 /* find the lowest layer that this region can go on */
2406 size_t j = layers.size();
2408 /* try layer j - 1; it can go on if it overlaps no other region
2409 that is already on that layer
2412 bool overlap = false;
2413 for (int k = start_division; k <= end_division; ++k) {
2414 RegionList::iterator l = layers[j-1][k].begin ();
2415 while (l != layers[j-1][k].end()) {
2416 if ((*l)->overlap_equivalent (*i)) {
2429 /* overlap, so we must use layer j */
2436 if (j == layers.size()) {
2437 /* we need a new layer for this region */
2438 layers.push_back (vector<RegionList> (divisions));
2441 /* put a reference to this region in each of the divisions that it exists in */
2442 for (int k = start_division; k <= end_division; ++k) {
2443 layers[j][k].push_back (*i);
2446 (*i)->set_layer (j);
2449 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2450 relayering because we just removed the only region on the top layer, nothing will
2451 appear to have changed, but the StreamView must still sort itself out. We could
2452 probably keep a note of the top layer last time we relayered, and check that,
2453 but premature optimisation &c...
2455 notify_layering_changed ();
2457 /* This relayer() may have been called as a result of a region removal, in which
2458 case we need to setup layering indices to account for the one that has just
2461 setup_layering_indices (copy);
2465 Playlist::raise_region (boost::shared_ptr<Region> region)
2467 set_layer (region, region->layer() + 1.5);
2472 Playlist::lower_region (boost::shared_ptr<Region> region)
2474 set_layer (region, region->layer() - 1.5);
2479 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2481 set_layer (region, DBL_MAX);
2486 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2488 set_layer (region, -0.5);
2493 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2495 RegionList::iterator i;
2501 RegionWriteLock rlock (const_cast<Playlist *> (this));
2503 for (i = regions.begin(); i != regions.end(); ++i) {
2505 if ((*i)->position() >= start) {
2511 if ((*i)->last_frame() > max_framepos - distance) {
2512 new_pos = max_framepos - (*i)->length();
2514 new_pos = (*i)->position() + distance;
2519 if ((*i)->position() > distance) {
2520 new_pos = (*i)->position() - distance;
2526 (*i)->set_position (new_pos);
2534 notify_contents_changed ();
2540 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2542 RegionReadLock rlock (const_cast<Playlist*> (this));
2544 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2545 if ((*r)->uses_source (src)) {
2553 boost::shared_ptr<Region>
2554 Playlist::find_region (const ID& id) const
2556 RegionReadLock rlock (const_cast<Playlist*> (this));
2558 /* searches all regions currently in use by the playlist */
2560 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2561 if ((*i)->id() == id) {
2566 return boost::shared_ptr<Region> ();
2570 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2572 RegionReadLock rlock (const_cast<Playlist*> (this));
2575 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2584 boost::shared_ptr<Region>
2585 Playlist::region_by_id (const ID& id) const
2587 /* searches all regions ever added to this playlist */
2589 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2590 if ((*i)->id() == id) {
2594 return boost::shared_ptr<Region> ();
2598 Playlist::dump () const
2600 boost::shared_ptr<Region> r;
2602 cerr << "Playlist \"" << _name << "\" " << endl
2603 << regions.size() << " regions "
2606 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2608 cerr << " " << r->name() << " ["
2609 << r->start() << "+" << r->length()
2619 Playlist::set_frozen (bool yn)
2625 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2629 if (region->locked()) {
2636 RegionWriteLock rlock (const_cast<Playlist*> (this));
2641 RegionList::iterator next;
2643 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2644 if ((*i) == region) {
2648 if (next != regions.end()) {
2650 if ((*next)->locked()) {
2656 if ((*next)->position() != region->last_frame() + 1) {
2657 /* they didn't used to touch, so after shuffle,
2658 just have them swap positions.
2660 new_pos = (*next)->position();
2662 /* they used to touch, so after shuffle,
2663 make sure they still do. put the earlier
2664 region where the later one will end after
2667 new_pos = region->position() + (*next)->length();
2670 (*next)->set_position (region->position());
2671 region->set_position (new_pos);
2673 /* avoid a full sort */
2675 regions.erase (i); // removes the region from the list */
2677 regions.insert (next, region); // adds it back after next
2686 RegionList::iterator prev = regions.end();
2688 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2689 if ((*i) == region) {
2691 if (prev != regions.end()) {
2693 if ((*prev)->locked()) {
2698 if (region->position() != (*prev)->last_frame() + 1) {
2699 /* they didn't used to touch, so after shuffle,
2700 just have them swap positions.
2702 new_pos = region->position();
2704 /* they used to touch, so after shuffle,
2705 make sure they still do. put the earlier
2706 one where the later one will end after
2708 new_pos = (*prev)->position() + region->length();
2711 region->set_position ((*prev)->position());
2712 (*prev)->set_position (new_pos);
2714 /* avoid a full sort */
2716 regions.erase (i); // remove region
2717 regions.insert (prev, region); // insert region before prev
2733 notify_contents_changed();
2739 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2741 RegionReadLock rlock (const_cast<Playlist*> (this));
2743 if (regions.size() > 1) {
2751 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2753 ripple_locked (at, distance, exclude);
2757 Playlist::update_after_tempo_map_change ()
2759 RegionWriteLock rlock (const_cast<Playlist*> (this));
2760 RegionList copy (regions.rlist());
2764 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2765 (*i)->update_after_tempo_map_change ();
2772 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2774 RegionWriteLock rl (this, false);
2775 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2781 Playlist::has_region_at (framepos_t const p) const
2783 RegionReadLock (const_cast<Playlist *> (this));
2785 RegionList::const_iterator i = regions.begin ();
2786 while (i != regions.end() && !(*i)->covers (p)) {
2790 return (i != regions.end());
2793 /** Remove any region that uses a given source */
2795 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2797 RegionWriteLock rl (this);
2799 RegionList::iterator i = regions.begin();
2800 while (i != regions.end()) {
2801 RegionList::iterator j = i;
2804 if ((*i)->uses_source (s)) {
2805 remove_region_internal (*i);
2812 /** Look from a session frame time and find the start time of the next region
2813 * which is on the top layer of this playlist.
2814 * @param t Time to look from.
2815 * @return Position of next top-layered region, or max_framepos if there isn't one.
2818 Playlist::find_next_top_layer_position (framepos_t t) const
2820 RegionReadLock rlock (const_cast<Playlist *> (this));
2822 layer_t const top = top_layer ();
2824 RegionList copy = regions.rlist ();
2825 copy.sort (RegionSortByPosition ());
2827 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2828 if ((*i)->position() >= t && (*i)->layer() == top) {
2829 return (*i)->position();
2833 return max_framepos;
2836 boost::shared_ptr<Region>
2837 Playlist::combine (const RegionList& r)
2840 uint32_t channels = 0;
2842 framepos_t earliest_position = max_framepos;
2843 vector<TwoRegions> old_and_new_regions;
2844 vector<boost::shared_ptr<Region> > originals;
2845 vector<boost::shared_ptr<Region> > copies;
2848 uint32_t max_level = 0;
2850 /* find the maximum depth of all the regions we're combining */
2852 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2853 max_level = max (max_level, (*i)->max_source_level());
2856 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2857 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2859 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2861 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2862 earliest_position = min (earliest_position, (*i)->position());
2865 /* enable this so that we do not try to create xfades etc. as we add
2869 pl->in_partition = true;
2871 /* sort by position then layer.
2872 * route_time_axis passes 'selected_regions' - which is not sorted.
2873 * here we need the top-most first, then every layer's region sorted by position.
2875 RegionList sorted(r);
2876 sorted.sort(RegionSortByLayerAndPosition());
2878 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
2880 /* copy the region */
2882 boost::shared_ptr<Region> original_region = (*i);
2883 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2885 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2886 originals.push_back (original_region);
2887 copies.push_back (copied_region);
2889 RegionFactory::add_compound_association (original_region, copied_region);
2891 /* make position relative to zero */
2893 pl->add_region (copied_region, original_region->position() - earliest_position);
2894 copied_region->set_layer (original_region->layer ());
2896 /* use the maximum number of channels for any region */
2898 channels = max (channels, original_region->n_channels());
2900 /* it will go above the layer of the highest existing region */
2902 layer = max (layer, original_region->layer());
2905 pl->in_partition = false;
2907 pre_combine (copies);
2909 /* now create a new PlaylistSource for each channel in the new playlist */
2912 pair<framepos_t,framepos_t> extent = pl->get_extent();
2914 for (uint32_t chn = 0; chn < channels; ++chn) {
2915 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2919 /* now a new whole-file region using the list of sources */
2921 plist.add (Properties::start, 0);
2922 plist.add (Properties::length, extent.second);
2923 plist.add (Properties::name, parent_name);
2924 plist.add (Properties::whole_file, true);
2926 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2928 /* now the non-whole-file region that we will actually use in the
2933 plist.add (Properties::start, 0);
2934 plist.add (Properties::length, extent.second);
2935 plist.add (Properties::name, child_name);
2936 plist.add (Properties::layer, layer+1);
2938 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2940 /* remove all the selected regions from the current playlist
2945 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2949 /* do type-specific stuff with the originals and the new compound
2953 post_combine (originals, compound_region);
2955 /* add the new region at the right location */
2957 add_region (compound_region, earliest_position);
2963 return compound_region;
2967 Playlist::uncombine (boost::shared_ptr<Region> target)
2969 boost::shared_ptr<PlaylistSource> pls;
2970 boost::shared_ptr<const Playlist> pl;
2971 vector<boost::shared_ptr<Region> > originals;
2972 vector<TwoRegions> old_and_new_regions;
2974 // (1) check that its really a compound region
2976 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
2980 pl = pls->playlist();
2982 framepos_t adjusted_start = 0; // gcc isn't smart enough
2983 framepos_t adjusted_end = 0; // gcc isn't smart enough
2985 /* the leftmost (earliest) edge of the compound region
2986 starts at zero in its source, or larger if it
2987 has been trimmed or content-scrolled.
2989 the rightmost (latest) edge of the compound region
2990 relative to its source is the starting point plus
2991 the length of the region.
2994 // (2) get all the original regions
2996 const RegionList& rl (pl->region_list().rlist());
2997 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2998 frameoffset_t move_offset = 0;
3000 /* there are two possibilities here:
3001 1) the playlist that the playlist source was based on
3002 is us, so just add the originals (which belonged to
3003 us anyway) back in the right place.
3005 2) the playlist that the playlist source was based on
3006 is NOT us, so we need to make copies of each of
3007 the original regions that we find, and add them
3010 bool same_playlist = (pls->original() == id());
3012 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3014 boost::shared_ptr<Region> current (*i);
3016 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3018 if (ca == cassocs.end()) {
3022 boost::shared_ptr<Region> original (ca->second);
3023 bool modified_region;
3025 if (i == rl.begin()) {
3026 move_offset = (target->position() - original->position()) - target->start();
3027 adjusted_start = original->position() + target->start();
3028 adjusted_end = adjusted_start + target->length();
3031 if (!same_playlist) {
3032 framepos_t pos = original->position();
3033 /* make a copy, but don't announce it */
3034 original = RegionFactory::create (original, false);
3035 /* the pure copy constructor resets position() to zero,
3038 original->set_position (pos);
3041 /* check to see how the original region (in the
3042 * playlist before compounding occured) overlaps
3043 * with the new state of the compound region.
3046 original->clear_changes ();
3047 modified_region = false;
3049 switch (original->coverage (adjusted_start, adjusted_end)) {
3050 case Evoral::OverlapNone:
3051 /* original region does not cover any part
3052 of the current state of the compound region
3056 case Evoral::OverlapInternal:
3057 /* overlap is just a small piece inside the
3058 * original so trim both ends
3060 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3061 modified_region = true;
3064 case Evoral::OverlapExternal:
3065 /* overlap fully covers original, so leave it
3070 case Evoral::OverlapEnd:
3071 /* overlap starts within but covers end,
3072 so trim the front of the region
3074 original->trim_front (adjusted_start);
3075 modified_region = true;
3078 case Evoral::OverlapStart:
3079 /* overlap covers start but ends within, so
3080 * trim the end of the region.
3082 original->trim_end (adjusted_end);
3083 modified_region = true;
3088 /* fix the position to match any movement of the compound region.
3090 original->set_position (original->position() + move_offset);
3091 modified_region = true;
3094 if (modified_region) {
3095 _session.add_command (new StatefulDiffCommand (original));
3098 /* and add to the list of regions waiting to be
3102 originals.push_back (original);
3103 old_and_new_regions.push_back (TwoRegions (*i, original));
3106 pre_uncombine (originals, target);
3108 in_partition = true;
3111 // (3) remove the compound region
3113 remove_region (target);
3115 // (4) add the constituent regions
3117 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3118 add_region ((*i), (*i)->position());
3119 set_layer((*i), (*i)->layer());
3122 in_partition = false;
3127 Playlist::fade_range (list<AudioRange>& ranges)
3129 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3130 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3131 (*i)->fade_range ((*r).start, (*r).end);
3137 Playlist::max_source_level () const
3139 RegionReadLock rlock (const_cast<Playlist *> (this));
3142 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3143 lvl = max (lvl, (*i)->max_source_level());
3150 Playlist::set_orig_track_id (const PBD::ID& id)
3152 _orig_track_id = id;
3155 /** Take a list of ranges, coalesce any that can be coalesced, then call
3156 * check_crossfades for each one.
3159 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3161 /* XXX: it's a shame that this coalesce algorithm also exists in
3162 TimeSelection::consolidate().
3165 /* XXX: xfade: this is implemented in Evoral::RangeList */
3168 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3169 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3175 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3176 i->from = min (i->from, j->from);
3177 i->to = max (i->to, j->to);
3186 Playlist::set_capture_insertion_in_progress (bool yn)
3188 _capture_insertion_underway = yn;