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);
259 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
260 //at the end of construction, any length of cnt beyond the extents of the regions is end_space
261 _end_space = cnt - (get_extent().second - get_extent().first);
264 first_set_state = false;
271 InUse (true); /* EMIT SIGNAL */
282 InUse (false); /* EMIT SIGNAL */
287 Playlist::copy_regions (RegionList& newlist) const
289 RegionReadLock rlock (const_cast<Playlist *> (this));
291 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
292 newlist.push_back (RegionFactory::create (*i, true));
297 Playlist::init (bool hide)
299 add_property (regions);
300 _xml_node_name = X_("Playlist");
302 g_atomic_int_set (&block_notifications, 0);
303 g_atomic_int_set (&ignore_state_changes, 0);
304 pending_contents_change = false;
305 pending_layering = false;
306 first_set_state = true;
315 _edit_mode = Config->get_edit_mode();
317 in_partition = false;
320 _capture_insertion_underway = false;
324 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
325 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
327 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
330 Playlist::~Playlist ()
332 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
335 RegionReadLock rl (this);
337 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
338 (*i)->set_playlist (boost::shared_ptr<Playlist>());
342 /* GoingAway must be emitted by derived classes */
346 Playlist::_set_sort_id ()
349 Playlists are given names like <track name>.<id>
350 or <track name>.<edit group name>.<id> where id
351 is an integer. We extract the id and sort by that.
354 size_t dot_position = _name.val().find_last_of(".");
356 if (dot_position == string::npos) {
359 string t = _name.val().substr(dot_position + 1);
362 _sort_id = boost::lexical_cast<int>(t);
365 catch (boost::bad_lexical_cast e) {
372 Playlist::set_name (const string& str)
374 /* in a typical situation, a playlist is being used
375 by one diskstream and also is referenced by the
376 Session. if there are more references than that,
377 then don't change the name.
384 bool ret = SessionObject::set_name(str);
391 /***********************************************************************
392 CHANGE NOTIFICATION HANDLING
394 Notifications must be delayed till the region_lock is released. This
395 is necessary because handlers for the signals may need to acquire
396 the lock (e.g. to read from the playlist).
397 ***********************************************************************/
400 Playlist::begin_undo ()
407 Playlist::end_undo ()
416 delay_notifications ();
417 g_atomic_int_inc (&ignore_state_changes);
420 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
422 Playlist::thaw (bool from_undo)
424 g_atomic_int_dec_and_test (&ignore_state_changes);
425 release_notifications (from_undo);
430 Playlist::delay_notifications ()
432 g_atomic_int_inc (&block_notifications);
435 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
437 Playlist::release_notifications (bool from_undo)
439 if (g_atomic_int_dec_and_test (&block_notifications)) {
440 flush_notifications (from_undo);
445 Playlist::notify_contents_changed ()
447 if (holding_state ()) {
448 pending_contents_change = true;
450 pending_contents_change = false;
451 ContentsChanged(); /* EMIT SIGNAL */
456 Playlist::notify_layering_changed ()
458 if (holding_state ()) {
459 pending_layering = true;
461 pending_layering = false;
462 LayeringChanged(); /* EMIT SIGNAL */
467 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
469 if (holding_state ()) {
470 pending_removes.insert (r);
471 pending_contents_change = true;
473 /* this might not be true, but we have to act
474 as though it could be.
476 pending_contents_change = false;
477 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
478 ContentsChanged (); /* EMIT SIGNAL */
483 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
485 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
487 if (holding_state ()) {
489 pending_range_moves.push_back (move);
493 list< Evoral::RangeMove<framepos_t> > m;
495 RangesMoved (m, false);
501 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
503 if (r->position() >= r->last_position()) {
504 /* trimmed shorter */
508 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
510 if (holding_state ()) {
512 pending_region_extensions.push_back (extra);
516 list<Evoral::Range<framepos_t> > r;
524 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
526 if (r->length() < r->last_length()) {
527 /* trimmed shorter */
530 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
532 if (holding_state ()) {
534 pending_region_extensions.push_back (extra);
538 list<Evoral::Range<framepos_t> > r;
546 Playlist::notify_region_added (boost::shared_ptr<Region> r)
548 /* the length change might not be true, but we have to act
549 as though it could be.
552 if (holding_state()) {
553 pending_adds.insert (r);
554 pending_contents_change = true;
557 pending_contents_change = false;
558 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
559 ContentsChanged (); /* EMIT SIGNAL */
563 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
565 Playlist::flush_notifications (bool from_undo)
567 set<boost::shared_ptr<Region> >::iterator s;
568 bool regions_changed = false;
576 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
577 regions_changed = true;
580 /* XXX: it'd be nice if we could use pending_bounds for
581 RegionsExtended and RegionsMoved.
584 /* we have no idea what order the regions ended up in pending
585 bounds (it could be based on selection order, for example).
586 so, to preserve layering in the "most recently moved is higher"
587 model, sort them by existing layer, then timestamp them.
590 // RegionSortByLayer cmp;
591 // pending_bounds.sort (cmp);
593 list<Evoral::Range<framepos_t> > crossfade_ranges;
595 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
596 crossfade_ranges.push_back ((*r)->last_range ());
597 crossfade_ranges.push_back ((*r)->range ());
600 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
601 crossfade_ranges.push_back ((*s)->range ());
602 remove_dependents (*s);
603 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
606 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
607 crossfade_ranges.push_back ((*s)->range ());
608 /* don't emit RegionAdded signal until relayering is done,
609 so that the region is fully setup by the time
610 anyone hears that its been added
614 if (((regions_changed || pending_contents_change) && !in_set_state) || pending_layering) {
618 if (regions_changed || pending_contents_change) {
619 pending_contents_change = false;
620 ContentsChanged (); /* EMIT SIGNAL */
623 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
624 (*s)->clear_changes ();
625 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
628 coalesce_and_check_crossfades (crossfade_ranges);
630 if (!pending_range_moves.empty ()) {
631 /* We don't need to check crossfades for these as pending_bounds has
634 RangesMoved (pending_range_moves, from_undo);
637 if (!pending_region_extensions.empty ()) {
638 RegionsExtended (pending_region_extensions);
647 Playlist::clear_pending ()
649 pending_adds.clear ();
650 pending_removes.clear ();
651 pending_bounds.clear ();
652 pending_range_moves.clear ();
653 pending_region_extensions.clear ();
654 pending_contents_change = false;
657 /*************************************************************
659 *************************************************************/
661 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
663 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
665 RegionWriteLock rlock (this);
666 times = fabs (times);
668 int itimes = (int) floor (times);
670 framepos_t pos = position;
672 if (times == 1 && auto_partition){
673 partition(pos - 1, (pos + region->length()), true);
677 add_region_internal (region, pos);
678 set_layer (region, DBL_MAX);
679 pos += region->length();
684 /* note that itimes can be zero if we being asked to just
685 insert a single fraction of the region.
688 for (int i = 0; i < itimes; ++i) {
689 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
690 add_region_internal (copy, pos);
691 set_layer (copy, DBL_MAX);
692 pos += region->length();
695 framecnt_t length = 0;
697 if (floor (times) != times) {
698 length = (framecnt_t) floor (region->length() * (times - floor (times)));
700 RegionFactory::region_name (name, region->name(), false);
705 plist.add (Properties::start, region->start());
706 plist.add (Properties::length, length);
707 plist.add (Properties::name, name);
708 plist.add (Properties::layer, region->layer());
710 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
711 add_region_internal (sub, pos);
712 set_layer (sub, DBL_MAX);
716 possibly_splice_unlocked (position, (pos + length) - position, region);
720 Playlist::set_region_ownership ()
722 RegionWriteLock rl (this);
723 RegionList::iterator i;
724 boost::weak_ptr<Playlist> pl (shared_from_this());
726 for (i = regions.begin(); i != regions.end(); ++i) {
727 (*i)->set_playlist (pl);
732 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
734 if (region->data_type() != _type) {
738 RegionSortByPosition cmp;
740 if (!first_set_state) {
741 boost::shared_ptr<Playlist> foo (shared_from_this());
742 region->set_playlist (boost::weak_ptr<Playlist>(foo));
745 region->set_position (position);
747 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
748 all_regions.insert (region);
750 possibly_splice_unlocked (position, region->length(), region);
752 if (!holding_state ()) {
753 /* layers get assigned from XML state, and are not reset during undo/redo */
757 /* we need to notify the existence of new region before checking dependents. Ick. */
759 notify_region_added (region);
761 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
767 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
769 RegionWriteLock rlock (this);
771 bool old_sp = _splicing;
774 remove_region_internal (old);
775 add_region_internal (newr, pos);
776 set_layer (newr, old->layer ());
780 possibly_splice_unlocked (pos, old->length() - newr->length());
784 Playlist::remove_region (boost::shared_ptr<Region> region)
786 RegionWriteLock rlock (this);
787 remove_region_internal (region);
791 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
793 RegionList::iterator i;
797 region->set_playlist (boost::weak_ptr<Playlist>());
800 /* XXX should probably freeze here .... */
802 for (i = regions.begin(); i != regions.end(); ++i) {
805 framepos_t pos = (*i)->position();
806 framecnt_t distance = (*i)->length();
810 possibly_splice_unlocked (pos, -distance);
812 if (!holding_state ()) {
814 remove_dependents (region);
817 notify_region_removed (region);
826 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
828 if (Config->get_use_overlap_equivalency()) {
829 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
830 if ((*i)->overlap_equivalent (other)) {
831 results.push_back (*i);
835 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
836 if ((*i)->equivalent (other)) {
837 results.push_back (*i);
844 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
846 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
848 if ((*i) && (*i)->region_list_equivalent (other)) {
849 results.push_back (*i);
855 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
857 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
859 if ((*i) && (*i)->any_source_equivalent (other)) {
860 results.push_back (*i);
866 Playlist::partition (framepos_t start, framepos_t end, bool cut)
870 partition_internal (start, end, cut, thawlist);
872 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
873 (*i)->resume_property_changes ();
877 /** Go through each region on the playlist and cut them at start and end, removing the section between
878 * start and end if cutting == true. Regions that lie entirely within start and end are always
883 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
885 RegionList new_regions;
888 RegionWriteLock rlock (this);
890 boost::shared_ptr<Region> region;
891 boost::shared_ptr<Region> current;
893 RegionList::iterator tmp;
894 Evoral::OverlapType overlap;
895 framepos_t pos1, pos2, pos3, pos4;
899 /* need to work from a copy, because otherwise the regions we add during the process
900 get operated on as well.
903 RegionList copy = regions.rlist();
905 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
912 if (current->first_frame() >= start && current->last_frame() < end) {
915 remove_region_internal (current);
921 /* coverage will return OverlapStart if the start coincides
922 with the end point. we do not partition such a region,
923 so catch this special case.
926 if (current->first_frame() >= end) {
930 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
934 pos1 = current->position();
937 pos4 = current->last_frame();
939 if (overlap == Evoral::OverlapInternal) {
940 /* split: we need 3 new regions, the front, middle and end.
941 cut: we need 2 regions, the front and end.
946 ---------------*************************------------
949 ---------------*****++++++++++++++++====------------
951 ---------------*****----------------====------------
956 /* "middle" ++++++ */
958 RegionFactory::region_name (new_name, current->name(), false);
962 plist.add (Properties::start, current->start() + (pos2 - pos1));
963 plist.add (Properties::length, pos3 - pos2);
964 plist.add (Properties::name, new_name);
965 plist.add (Properties::layer, current->layer ());
966 plist.add (Properties::layering_index, current->layering_index ());
967 plist.add (Properties::automatic, true);
968 plist.add (Properties::left_of_split, true);
969 plist.add (Properties::right_of_split, true);
971 region = RegionFactory::create (current, plist);
972 add_region_internal (region, start);
973 new_regions.push_back (region);
978 RegionFactory::region_name (new_name, current->name(), false);
982 plist.add (Properties::start, current->start() + (pos3 - pos1));
983 plist.add (Properties::length, pos4 - pos3);
984 plist.add (Properties::name, new_name);
985 plist.add (Properties::layer, current->layer ());
986 plist.add (Properties::layering_index, current->layering_index ());
987 plist.add (Properties::automatic, true);
988 plist.add (Properties::right_of_split, true);
990 region = RegionFactory::create (current, plist);
992 add_region_internal (region, end);
993 new_regions.push_back (region);
997 current->suspend_property_changes ();
998 thawlist.push_back (current);
999 current->cut_end (pos2 - 1);
1001 } else if (overlap == Evoral::OverlapEnd) {
1005 ---------------*************************------------
1008 ---------------**************+++++++++++------------
1010 ---------------**************-----------------------
1017 RegionFactory::region_name (new_name, current->name(), false);
1021 plist.add (Properties::start, current->start() + (pos2 - pos1));
1022 plist.add (Properties::length, pos4 - pos2);
1023 plist.add (Properties::name, new_name);
1024 plist.add (Properties::layer, current->layer ());
1025 plist.add (Properties::layering_index, current->layering_index ());
1026 plist.add (Properties::automatic, true);
1027 plist.add (Properties::left_of_split, true);
1029 region = RegionFactory::create (current, plist);
1031 add_region_internal (region, start);
1032 new_regions.push_back (region);
1037 current->suspend_property_changes ();
1038 thawlist.push_back (current);
1039 current->cut_end (pos2 - 1);
1041 } else if (overlap == Evoral::OverlapStart) {
1043 /* split: we need 2 regions: the front and the end.
1044 cut: just trim current to skip the cut area
1049 ---------------*************************------------
1053 ---------------****+++++++++++++++++++++------------
1055 -------------------*********************------------
1061 RegionFactory::region_name (new_name, current->name(), false);
1065 plist.add (Properties::start, current->start());
1066 plist.add (Properties::length, pos3 - pos1);
1067 plist.add (Properties::name, new_name);
1068 plist.add (Properties::layer, current->layer ());
1069 plist.add (Properties::layering_index, current->layering_index ());
1070 plist.add (Properties::automatic, true);
1071 plist.add (Properties::right_of_split, true);
1073 region = RegionFactory::create (current, plist);
1075 add_region_internal (region, pos1);
1076 new_regions.push_back (region);
1081 current->suspend_property_changes ();
1082 thawlist.push_back (current);
1083 current->trim_front (pos3);
1084 } else if (overlap == Evoral::OverlapExternal) {
1086 /* split: no split required.
1087 cut: remove the region.
1092 ---------------*************************------------
1096 ---------------*************************------------
1098 ----------------------------------------------------
1103 remove_region_internal (current);
1106 new_regions.push_back (current);
1110 in_partition = false;
1113 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1114 framepos_t wanted_length = end-start;
1115 _end_space = wanted_length - get_extent().second-get_extent().first;
1118 boost::shared_ptr<Playlist>
1119 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1121 boost::shared_ptr<Playlist> ret;
1122 boost::shared_ptr<Playlist> pl;
1125 if (ranges.empty()) {
1126 return boost::shared_ptr<Playlist>();
1129 start = ranges.front().start;
1131 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1133 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1135 if (i == ranges.begin()) {
1139 /* paste the next section into the nascent playlist,
1140 offset to reflect the start of the first range we
1144 ret->paste (pl, (*i).start - start, 1.0f);
1151 boost::shared_ptr<Playlist>
1152 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1154 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1155 return cut_copy (pmf, ranges, result_is_hidden);
1158 boost::shared_ptr<Playlist>
1159 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1161 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1162 return cut_copy (pmf, ranges, result_is_hidden);
1165 boost::shared_ptr<Playlist>
1166 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1168 boost::shared_ptr<Playlist> the_copy;
1169 RegionList thawlist;
1172 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1173 string new_name = _name;
1177 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1178 return boost::shared_ptr<Playlist>();
1181 partition_internal (start, start+cnt-1, true, thawlist);
1183 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1184 (*i)->resume_property_changes();
1190 boost::shared_ptr<Playlist>
1191 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1195 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1196 string new_name = _name;
1200 // cnt = min (_get_extent().second - start, cnt); (We need the full range length when copy/pasting in Ripple. Why was this limit here? It's not in CUT... )
1202 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1206 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1208 times = fabs (times);
1211 RegionReadLock rl2 (other.get());
1213 int itimes = (int) floor (times);
1214 framepos_t pos = position;
1215 framecnt_t const shift = other->_get_extent().second;
1216 layer_t top = top_layer ();
1219 RegionWriteLock rl1 (this);
1221 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1222 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1224 /* put these new regions on top of all existing ones, but preserve
1225 the ordering they had in the original playlist.
1228 add_region_internal (copy_of_region, (*i)->position() + pos);
1229 set_layer (copy_of_region, copy_of_region->layer() + top);
1241 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1243 times = fabs (times);
1245 RegionWriteLock rl (this);
1246 int itimes = (int) floor (times);
1247 framepos_t pos = position + 1;
1250 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1251 add_region_internal (copy, pos);
1252 set_layer (copy, DBL_MAX);
1253 pos += region->length();
1256 if (floor (times) != times) {
1257 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1259 RegionFactory::region_name (name, region->name(), false);
1264 plist.add (Properties::start, region->start());
1265 plist.add (Properties::length, length);
1266 plist.add (Properties::name, name);
1268 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1269 add_region_internal (sub, pos);
1270 set_layer (sub, DBL_MAX);
1276 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1278 RegionWriteLock rlock (this);
1279 RegionList copy (regions.rlist());
1282 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1284 if ((*r)->last_frame() < at) {
1289 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1290 /* intersected region */
1291 if (!move_intersected) {
1296 /* do not move regions glued to music time - that
1297 has to be done separately.
1300 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1301 fixup.push_back (*r);
1305 (*r)->set_position ((*r)->position() + distance);
1308 /* XXX: may not be necessary; Region::post_set should do this, I think */
1309 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1310 (*r)->recompute_position_from_lock_style ();
1315 Playlist::split (framepos_t at)
1317 RegionWriteLock rlock (this);
1318 RegionList copy (regions.rlist());
1320 /* use a copy since this operation can modify the region list
1323 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1324 _split_region (*r, at);
1329 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1331 RegionWriteLock rl (this);
1332 _split_region (region, playlist_position);
1336 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1338 if (!region->covers (playlist_position)) {
1342 if (region->position() == playlist_position ||
1343 region->last_frame() == playlist_position) {
1347 boost::shared_ptr<Region> left;
1348 boost::shared_ptr<Region> right;
1349 frameoffset_t before;
1350 frameoffset_t after;
1354 /* split doesn't change anything about length, so don't try to splice */
1356 bool old_sp = _splicing;
1359 before = playlist_position - region->position();
1360 after = region->length() - before;
1362 RegionFactory::region_name (before_name, region->name(), false);
1367 plist.add (Properties::position, region->position ());
1368 plist.add (Properties::length, before);
1369 plist.add (Properties::name, before_name);
1370 plist.add (Properties::left_of_split, true);
1371 plist.add (Properties::layering_index, region->layering_index ());
1372 plist.add (Properties::layer, region->layer ());
1374 /* note: we must use the version of ::create with an offset here,
1375 since it supplies that offset to the Region constructor, which
1376 is necessary to get audio region gain envelopes right.
1378 left = RegionFactory::create (region, 0, plist);
1381 RegionFactory::region_name (after_name, region->name(), false);
1386 plist.add (Properties::position, region->position() + before);
1387 plist.add (Properties::length, after);
1388 plist.add (Properties::name, after_name);
1389 plist.add (Properties::right_of_split, true);
1390 plist.add (Properties::layering_index, region->layering_index ());
1391 plist.add (Properties::layer, region->layer ());
1393 /* same note as above */
1394 right = RegionFactory::create (region, before, plist);
1397 add_region_internal (left, region->position());
1398 add_region_internal (right, region->position() + before);
1399 remove_region_internal (region);
1405 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1407 if (_splicing || in_set_state) {
1408 /* don't respond to splicing moves or state setting */
1412 if (_edit_mode == Splice) {
1413 splice_locked (at, distance, exclude);
1418 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1420 if (_splicing || in_set_state) {
1421 /* don't respond to splicing moves or state setting */
1425 if (_edit_mode == Splice) {
1426 splice_unlocked (at, distance, exclude);
1431 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1434 RegionWriteLock rl (this);
1435 core_splice (at, distance, exclude);
1440 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1442 core_splice (at, distance, exclude);
1446 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1450 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1452 if (exclude && (*i) == exclude) {
1456 if ((*i)->position() >= at) {
1457 framepos_t new_pos = (*i)->position() + distance;
1460 } else if (new_pos >= max_framepos - (*i)->length()) {
1461 new_pos = max_framepos - (*i)->length();
1464 (*i)->set_position (new_pos);
1470 notify_contents_changed ();
1474 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1477 RegionWriteLock rl (this);
1478 core_ripple (at, distance, exclude);
1483 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1485 core_ripple (at, distance, exclude);
1489 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1491 if (distance == 0) {
1496 RegionListProperty copy = regions;
1497 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1498 assert (i != copy.end());
1501 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1506 if ((*i)->position() >= at) {
1507 framepos_t new_pos = (*i)->position() + distance;
1508 framepos_t limit = max_framepos - (*i)->length();
1511 } else if (new_pos >= limit ) {
1515 (*i)->set_position (new_pos);
1520 notify_contents_changed ();
1525 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1527 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1531 if (what_changed.contains (Properties::position)) {
1533 /* remove it from the list then add it back in
1534 the right place again.
1537 RegionSortByPosition cmp;
1539 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1541 if (i == regions.end()) {
1542 /* the region bounds are being modified but its not currently
1543 in the region list. we will use its bounds correctly when/if
1550 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1553 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1555 frameoffset_t delta = 0;
1557 if (what_changed.contains (Properties::position)) {
1558 delta = region->position() - region->last_position();
1561 if (what_changed.contains (Properties::length)) {
1562 delta += region->length() - region->last_length();
1566 possibly_splice (region->last_position() + region->last_length(), delta, region);
1569 if (holding_state ()) {
1570 pending_bounds.push_back (region);
1572 notify_contents_changed ();
1574 list<Evoral::Range<framepos_t> > xf;
1575 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1576 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1577 coalesce_and_check_crossfades (xf);
1583 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1585 boost::shared_ptr<Region> region (weak_region.lock());
1591 /* this makes a virtual call to the right kind of playlist ... */
1593 region_changed (what_changed, region);
1597 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1599 PropertyChange our_interests;
1600 PropertyChange bounds;
1601 PropertyChange pos_and_length;
1604 if (in_set_state || in_flush) {
1608 our_interests.add (Properties::muted);
1609 our_interests.add (Properties::layer);
1610 our_interests.add (Properties::opaque);
1612 bounds.add (Properties::start);
1613 bounds.add (Properties::position);
1614 bounds.add (Properties::length);
1616 pos_and_length.add (Properties::position);
1617 pos_and_length.add (Properties::length);
1619 if (what_changed.contains (bounds)) {
1620 region_bounds_changed (what_changed, region);
1621 save = !(_splicing || _nudging);
1624 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1625 notify_region_moved (region);
1626 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1627 notify_region_end_trimmed (region);
1628 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1629 notify_region_start_trimmed (region);
1632 /* don't notify about layer changes, since we are the only object that can initiate
1633 them, and we notify in ::relayer()
1636 if (what_changed.contains (our_interests)) {
1644 Playlist::drop_regions ()
1646 RegionWriteLock rl (this);
1648 all_regions.clear ();
1652 Playlist::sync_all_regions_with_regions ()
1654 RegionWriteLock rl (this);
1656 all_regions.clear ();
1658 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1659 all_regions.insert (*i);
1664 Playlist::clear (bool with_signals)
1667 RegionWriteLock rl (this);
1669 region_state_changed_connections.drop_connections ();
1671 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1672 pending_removes.insert (*i);
1677 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1678 remove_dependents (*s);
1684 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1685 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1688 pending_removes.clear ();
1689 pending_contents_change = false;
1695 /***********************************************************************
1697 **********************************************************************/
1699 boost::shared_ptr<RegionList>
1700 Playlist::regions_at (framepos_t frame)
1702 RegionReadLock rlock (this);
1703 return find_regions_at (frame);
1707 Playlist::count_regions_at (framepos_t frame) const
1709 RegionReadLock rlock (const_cast<Playlist*>(this));
1712 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1713 if ((*i)->covers (frame)) {
1721 boost::shared_ptr<Region>
1722 Playlist::top_region_at (framepos_t frame)
1725 RegionReadLock rlock (this);
1726 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1727 boost::shared_ptr<Region> region;
1729 if (rlist->size()) {
1730 RegionSortByLayer cmp;
1732 region = rlist->back();
1738 boost::shared_ptr<Region>
1739 Playlist::top_unmuted_region_at (framepos_t frame)
1742 RegionReadLock rlock (this);
1743 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1745 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1747 RegionList::iterator tmp = i;
1750 if ((*i)->muted()) {
1757 boost::shared_ptr<Region> region;
1759 if (rlist->size()) {
1760 RegionSortByLayer cmp;
1762 region = rlist->back();
1768 boost::shared_ptr<RegionList>
1769 Playlist::find_regions_at (framepos_t frame)
1771 /* Caller must hold lock */
1773 boost::shared_ptr<RegionList> rlist (new RegionList);
1775 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1776 if ((*i)->covers (frame)) {
1777 rlist->push_back (*i);
1784 boost::shared_ptr<RegionList>
1785 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1787 RegionReadLock rlock (this);
1788 boost::shared_ptr<RegionList> rlist (new RegionList);
1790 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1791 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1792 rlist->push_back (*i);
1799 boost::shared_ptr<RegionList>
1800 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1802 RegionReadLock rlock (this);
1803 boost::shared_ptr<RegionList> rlist (new RegionList);
1805 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1806 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1807 rlist->push_back (*i);
1814 /** @param start Range start.
1815 * @param end Range end.
1816 * @return regions which have some part within this range.
1818 boost::shared_ptr<RegionList>
1819 Playlist::regions_touched (framepos_t start, framepos_t end)
1821 RegionReadLock rlock (this);
1822 return regions_touched_locked (start, end);
1825 boost::shared_ptr<RegionList>
1826 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1828 boost::shared_ptr<RegionList> rlist (new RegionList);
1830 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1831 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1832 rlist->push_back (*i);
1840 Playlist::find_next_transient (framepos_t from, int dir)
1842 RegionReadLock rlock (this);
1843 AnalysisFeatureList points;
1844 AnalysisFeatureList these_points;
1846 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1848 if ((*i)->last_frame() < from) {
1852 if ((*i)->first_frame() > from) {
1857 (*i)->get_transients (these_points);
1859 /* add first frame, just, err, because */
1861 these_points.push_back ((*i)->first_frame());
1863 points.insert (points.end(), these_points.begin(), these_points.end());
1864 these_points.clear ();
1867 if (points.empty()) {
1871 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1872 bool reached = false;
1875 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1880 if (reached && (*x) > from) {
1885 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1890 if (reached && (*x) < from) {
1899 boost::shared_ptr<Region>
1900 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1902 RegionReadLock rlock (this);
1903 boost::shared_ptr<Region> ret;
1904 framepos_t closest = max_framepos;
1906 bool end_iter = false;
1908 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1912 frameoffset_t distance;
1913 boost::shared_ptr<Region> r = (*i);
1918 pos = r->first_frame ();
1921 pos = r->last_frame ();
1924 pos = r->sync_position ();
1929 case 1: /* forwards */
1932 if ((distance = pos - frame) < closest) {
1941 default: /* backwards */
1944 if ((distance = frame - pos) < closest) {
1960 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1962 RegionReadLock rlock (this);
1964 framepos_t closest = max_framepos;
1965 framepos_t ret = -1;
1969 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1971 boost::shared_ptr<Region> r = (*i);
1972 frameoffset_t distance;
1974 if (r->first_frame() > frame) {
1976 distance = r->first_frame() - frame;
1978 if (distance < closest) {
1979 ret = r->first_frame();
1984 if (r->last_frame () > frame) {
1986 distance = r->last_frame () - frame;
1988 if (distance < closest) {
1989 ret = r->last_frame ();
1997 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1999 boost::shared_ptr<Region> r = (*i);
2000 frameoffset_t distance;
2002 if (r->last_frame() < frame) {
2004 distance = frame - r->last_frame();
2006 if (distance < closest) {
2007 ret = r->last_frame();
2012 if (r->first_frame() < frame) {
2014 distance = frame - r->first_frame();
2016 if (distance < closest) {
2017 ret = r->first_frame();
2028 /***********************************************************************/
2034 Playlist::mark_session_dirty ()
2036 if (!in_set_state && !holding_state ()) {
2037 _session.set_dirty();
2042 Playlist::rdiff (vector<Command*>& cmds) const
2044 RegionReadLock rlock (const_cast<Playlist *> (this));
2045 Stateful::rdiff (cmds);
2049 Playlist::clear_owned_changes ()
2051 RegionReadLock rlock (this);
2052 Stateful::clear_owned_changes ();
2056 Playlist::update (const RegionListProperty::ChangeRecord& change)
2058 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2059 name(), change.added.size(), change.removed.size()));
2062 /* add the added regions */
2063 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2064 add_region_internal ((*i), (*i)->position());
2066 /* remove the removed regions */
2067 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2075 Playlist::set_state (const XMLNode& node, int version)
2079 XMLNodeConstIterator niter;
2080 XMLPropertyList plist;
2081 XMLPropertyConstIterator piter;
2083 boost::shared_ptr<Region> region;
2085 bool seen_region_nodes = false;
2090 if (node.name() != "Playlist") {
2097 plist = node.properties();
2101 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2105 if (prop->name() == X_("name")) {
2106 _name = prop->value();
2108 } else if (prop->name() == X_("orig-diskstream-id")) {
2109 /* XXX legacy session: fix up later */
2110 _orig_track_id = prop->value ();
2111 } else if (prop->name() == X_("orig-track-id")) {
2112 _orig_track_id = prop->value ();
2113 } else if (prop->name() == X_("frozen")) {
2114 _frozen = string_is_affirmative (prop->value());
2115 } else if (prop->name() == X_("combine-ops")) {
2116 _combine_ops = atoi (prop->value());
2122 nlist = node.children();
2124 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2128 if (child->name() == "Region") {
2130 seen_region_nodes = true;
2132 if ((prop = child->property ("id")) == 0) {
2133 error << _("region state node has no ID, ignored") << endmsg;
2137 ID id = prop->value ();
2139 if ((region = region_by_id (id))) {
2141 region->suspend_property_changes ();
2143 if (region->set_state (*child, version)) {
2144 region->resume_property_changes ();
2148 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2149 region->suspend_property_changes ();
2151 error << _("Playlist: cannot create region from XML") << endmsg;
2156 RegionWriteLock rlock (this);
2157 add_region_internal (region, region->position());
2160 region->resume_property_changes ();
2165 if (seen_region_nodes && regions.empty()) {
2170 notify_contents_changed ();
2173 first_set_state = false;
2179 Playlist::get_state()
2181 return state (true);
2185 Playlist::get_template()
2187 return state (false);
2190 /** @param full_state true to include regions in the returned state, otherwise false.
2193 Playlist::state (bool full_state)
2195 XMLNode *node = new XMLNode (X_("Playlist"));
2198 node->add_property (X_("id"), id().to_s());
2199 node->add_property (X_("name"), _name);
2200 node->add_property (X_("type"), _type.to_string());
2202 _orig_track_id.print (buf, sizeof (buf));
2203 node->add_property (X_("orig-track-id"), buf);
2204 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2207 RegionReadLock rlock (this);
2209 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2210 node->add_property ("combine-ops", buf);
2212 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2213 node->add_child_nocopy ((*i)->get_state());
2218 node->add_child_copy (*_extra_xml);
2225 Playlist::empty() const
2227 RegionReadLock rlock (const_cast<Playlist *>(this));
2228 return regions.empty();
2232 Playlist::n_regions() const
2234 RegionReadLock rlock (const_cast<Playlist *>(this));
2235 return regions.size();
2238 /** @return true if the all_regions list is empty, ie this playlist
2239 * has never had a region added to it.
2242 Playlist::all_regions_empty() const
2244 RegionReadLock rl (const_cast<Playlist *> (this));
2245 return all_regions.empty();
2248 pair<framepos_t, framepos_t>
2249 Playlist::get_extent () const
2251 RegionReadLock rlock (const_cast<Playlist *>(this));
2252 return _get_extent ();
2255 pair<framepos_t, framepos_t>
2256 Playlist::get_extent_with_endspace () const
2258 pair<framepos_t, framepos_t> l = get_extent();
2259 l.second += _end_space;
2263 pair<framepos_t, framepos_t>
2264 Playlist::_get_extent () const
2266 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2268 if (regions.empty()) {
2273 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2274 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2275 if (e.first < ext.first) {
2276 ext.first = e.first;
2278 if (e.second > ext.second) {
2279 ext.second = e.second;
2287 Playlist::bump_name (string name, Session &session)
2289 string newname = name;
2292 newname = bump_name_once (newname, '.');
2293 } while (session.playlists->by_name (newname)!=NULL);
2300 Playlist::top_layer() const
2302 RegionReadLock rlock (const_cast<Playlist *> (this));
2305 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2306 top = max (top, (*i)->layer());
2312 Playlist::set_edit_mode (EditMode mode)
2317 struct RelayerSort {
2318 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2319 return a->layering_index() < b->layering_index();
2323 /** Set a new layer for a region. This adjusts the layering indices of all
2324 * regions in the playlist to put the specified region in the appropriate
2325 * place. The actual layering will be fixed up when relayer() happens.
2329 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2331 /* Remove the layer we are setting from our region list, and sort it */
2332 RegionList copy = regions.rlist();
2333 copy.remove (region);
2334 copy.sort (RelayerSort ());
2336 /* Put region back in the right place */
2337 RegionList::iterator i = copy.begin();
2338 while (i != copy.end ()) {
2339 if ((*i)->layer() > new_layer) {
2345 copy.insert (i, region);
2347 setup_layering_indices (copy);
2351 Playlist::setup_layering_indices (RegionList const & regions)
2354 list<Evoral::Range<framepos_t> > xf;
2356 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2357 (*k)->set_layering_index (j++);
2361 /** Take the layering indices of each of our regions, compute the layers
2362 * that they should be on, and write the layers back to the regions.
2365 Playlist::relayer ()
2367 /* never compute layers when setting from XML */
2373 /* Build up a new list of regions on each layer, stored in a set of lists
2374 each of which represent some period of time on some layer. The idea
2375 is to avoid having to search the entire region list to establish whether
2376 each region overlaps another */
2378 /* how many pieces to divide this playlist's time up into */
2379 int const divisions = 512;
2381 /* find the start and end positions of the regions on this playlist */
2382 framepos_t start = INT64_MAX;
2384 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2385 start = min (start, (*i)->position());
2386 end = max (end, (*i)->position() + (*i)->length());
2389 /* hence the size of each time division */
2390 double const division_size = (end - start) / double (divisions);
2392 vector<vector<RegionList> > layers;
2393 layers.push_back (vector<RegionList> (divisions));
2395 /* Sort our regions into layering index order */
2396 RegionList copy = regions.rlist();
2397 copy.sort (RelayerSort ());
2399 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2400 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2401 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2404 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2406 /* find the time divisions that this region covers; if there are no regions on the list,
2407 division_size will equal 0 and in this case we'll just say that
2408 start_division = end_division = 0.
2410 int start_division = 0;
2411 int end_division = 0;
2413 if (division_size > 0) {
2414 start_division = floor ( ((*i)->position() - start) / division_size);
2415 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2416 if (end_division == divisions) {
2421 assert (divisions == 0 || end_division < divisions);
2423 /* find the lowest layer that this region can go on */
2424 size_t j = layers.size();
2426 /* try layer j - 1; it can go on if it overlaps no other region
2427 that is already on that layer
2430 bool overlap = false;
2431 for (int k = start_division; k <= end_division; ++k) {
2432 RegionList::iterator l = layers[j-1][k].begin ();
2433 while (l != layers[j-1][k].end()) {
2434 if ((*l)->overlap_equivalent (*i)) {
2447 /* overlap, so we must use layer j */
2454 if (j == layers.size()) {
2455 /* we need a new layer for this region */
2456 layers.push_back (vector<RegionList> (divisions));
2459 /* put a reference to this region in each of the divisions that it exists in */
2460 for (int k = start_division; k <= end_division; ++k) {
2461 layers[j][k].push_back (*i);
2464 (*i)->set_layer (j);
2467 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2468 relayering because we just removed the only region on the top layer, nothing will
2469 appear to have changed, but the StreamView must still sort itself out. We could
2470 probably keep a note of the top layer last time we relayered, and check that,
2471 but premature optimisation &c...
2473 notify_layering_changed ();
2475 /* This relayer() may have been called as a result of a region removal, in which
2476 case we need to setup layering indices to account for the one that has just
2479 setup_layering_indices (copy);
2483 Playlist::raise_region (boost::shared_ptr<Region> region)
2485 set_layer (region, region->layer() + 1.5);
2490 Playlist::lower_region (boost::shared_ptr<Region> region)
2492 set_layer (region, region->layer() - 1.5);
2497 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2499 set_layer (region, DBL_MAX);
2504 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2506 set_layer (region, -0.5);
2511 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2513 RegionList::iterator i;
2519 RegionWriteLock rlock (const_cast<Playlist *> (this));
2521 for (i = regions.begin(); i != regions.end(); ++i) {
2523 if ((*i)->position() >= start) {
2529 if ((*i)->last_frame() > max_framepos - distance) {
2530 new_pos = max_framepos - (*i)->length();
2532 new_pos = (*i)->position() + distance;
2537 if ((*i)->position() > distance) {
2538 new_pos = (*i)->position() - distance;
2544 (*i)->set_position (new_pos);
2552 notify_contents_changed ();
2558 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2560 RegionReadLock rlock (const_cast<Playlist*> (this));
2562 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2563 if ((*r)->uses_source (src)) {
2571 boost::shared_ptr<Region>
2572 Playlist::find_region (const ID& id) const
2574 RegionReadLock rlock (const_cast<Playlist*> (this));
2576 /* searches all regions currently in use by the playlist */
2578 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2579 if ((*i)->id() == id) {
2584 return boost::shared_ptr<Region> ();
2588 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2590 RegionReadLock rlock (const_cast<Playlist*> (this));
2593 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2602 boost::shared_ptr<Region>
2603 Playlist::region_by_id (const ID& id) const
2605 /* searches all regions ever added to this playlist */
2607 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2608 if ((*i)->id() == id) {
2612 return boost::shared_ptr<Region> ();
2616 Playlist::dump () const
2618 boost::shared_ptr<Region> r;
2620 cerr << "Playlist \"" << _name << "\" " << endl
2621 << regions.size() << " regions "
2624 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2626 cerr << " " << r->name() << " ["
2627 << r->start() << "+" << r->length()
2637 Playlist::set_frozen (bool yn)
2643 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2647 if (region->locked()) {
2654 RegionWriteLock rlock (const_cast<Playlist*> (this));
2659 RegionList::iterator next;
2661 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2662 if ((*i) == region) {
2666 if (next != regions.end()) {
2668 if ((*next)->locked()) {
2674 if ((*next)->position() != region->last_frame() + 1) {
2675 /* they didn't used to touch, so after shuffle,
2676 just have them swap positions.
2678 new_pos = (*next)->position();
2680 /* they used to touch, so after shuffle,
2681 make sure they still do. put the earlier
2682 region where the later one will end after
2685 new_pos = region->position() + (*next)->length();
2688 (*next)->set_position (region->position());
2689 region->set_position (new_pos);
2691 /* avoid a full sort */
2693 regions.erase (i); // removes the region from the list */
2695 regions.insert (next, region); // adds it back after next
2704 RegionList::iterator prev = regions.end();
2706 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2707 if ((*i) == region) {
2709 if (prev != regions.end()) {
2711 if ((*prev)->locked()) {
2716 if (region->position() != (*prev)->last_frame() + 1) {
2717 /* they didn't used to touch, so after shuffle,
2718 just have them swap positions.
2720 new_pos = region->position();
2722 /* they used to touch, so after shuffle,
2723 make sure they still do. put the earlier
2724 one where the later one will end after
2726 new_pos = (*prev)->position() + region->length();
2729 region->set_position ((*prev)->position());
2730 (*prev)->set_position (new_pos);
2732 /* avoid a full sort */
2734 regions.erase (i); // remove region
2735 regions.insert (prev, region); // insert region before prev
2751 notify_contents_changed();
2757 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2759 RegionReadLock rlock (const_cast<Playlist*> (this));
2761 if (regions.size() > 1) {
2769 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2771 ripple_locked (at, distance, exclude);
2775 Playlist::update_after_tempo_map_change ()
2777 RegionWriteLock rlock (const_cast<Playlist*> (this));
2778 RegionList copy (regions.rlist());
2782 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2783 (*i)->update_after_tempo_map_change ();
2790 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2792 RegionWriteLock rl (this, false);
2793 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2799 Playlist::has_region_at (framepos_t const p) const
2801 RegionReadLock (const_cast<Playlist *> (this));
2803 RegionList::const_iterator i = regions.begin ();
2804 while (i != regions.end() && !(*i)->covers (p)) {
2808 return (i != regions.end());
2811 /** Remove any region that uses a given source */
2813 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2815 RegionWriteLock rl (this);
2817 RegionList::iterator i = regions.begin();
2818 while (i != regions.end()) {
2819 RegionList::iterator j = i;
2822 if ((*i)->uses_source (s)) {
2823 remove_region_internal (*i);
2830 /** Look from a session frame time and find the start time of the next region
2831 * which is on the top layer of this playlist.
2832 * @param t Time to look from.
2833 * @return Position of next top-layered region, or max_framepos if there isn't one.
2836 Playlist::find_next_top_layer_position (framepos_t t) const
2838 RegionReadLock rlock (const_cast<Playlist *> (this));
2840 layer_t const top = top_layer ();
2842 RegionList copy = regions.rlist ();
2843 copy.sort (RegionSortByPosition ());
2845 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2846 if ((*i)->position() >= t && (*i)->layer() == top) {
2847 return (*i)->position();
2851 return max_framepos;
2854 boost::shared_ptr<Region>
2855 Playlist::combine (const RegionList& r)
2858 uint32_t channels = 0;
2860 framepos_t earliest_position = max_framepos;
2861 vector<TwoRegions> old_and_new_regions;
2862 vector<boost::shared_ptr<Region> > originals;
2863 vector<boost::shared_ptr<Region> > copies;
2866 uint32_t max_level = 0;
2868 /* find the maximum depth of all the regions we're combining */
2870 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2871 max_level = max (max_level, (*i)->max_source_level());
2874 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2875 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2877 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2879 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2880 earliest_position = min (earliest_position, (*i)->position());
2883 /* enable this so that we do not try to create xfades etc. as we add
2887 pl->in_partition = true;
2889 /* sort by position then layer.
2890 * route_time_axis passes 'selected_regions' - which is not sorted.
2891 * here we need the top-most first, then every layer's region sorted by position.
2893 RegionList sorted(r);
2894 sorted.sort(RegionSortByLayerAndPosition());
2896 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
2898 /* copy the region */
2900 boost::shared_ptr<Region> original_region = (*i);
2901 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2903 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2904 originals.push_back (original_region);
2905 copies.push_back (copied_region);
2907 RegionFactory::add_compound_association (original_region, copied_region);
2909 /* make position relative to zero */
2911 pl->add_region (copied_region, original_region->position() - earliest_position);
2912 copied_region->set_layer (original_region->layer ());
2914 /* use the maximum number of channels for any region */
2916 channels = max (channels, original_region->n_channels());
2918 /* it will go above the layer of the highest existing region */
2920 layer = max (layer, original_region->layer());
2923 pl->in_partition = false;
2925 pre_combine (copies);
2927 /* now create a new PlaylistSource for each channel in the new playlist */
2930 pair<framepos_t,framepos_t> extent = pl->get_extent();
2932 for (uint32_t chn = 0; chn < channels; ++chn) {
2933 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2937 /* now a new whole-file region using the list of sources */
2939 plist.add (Properties::start, 0);
2940 plist.add (Properties::length, extent.second);
2941 plist.add (Properties::name, parent_name);
2942 plist.add (Properties::whole_file, true);
2944 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2946 /* now the non-whole-file region that we will actually use in the
2951 plist.add (Properties::start, 0);
2952 plist.add (Properties::length, extent.second);
2953 plist.add (Properties::name, child_name);
2954 plist.add (Properties::layer, layer+1);
2956 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2958 /* remove all the selected regions from the current playlist
2963 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2967 /* do type-specific stuff with the originals and the new compound
2971 post_combine (originals, compound_region);
2973 /* add the new region at the right location */
2975 add_region (compound_region, earliest_position);
2981 return compound_region;
2985 Playlist::uncombine (boost::shared_ptr<Region> target)
2987 boost::shared_ptr<PlaylistSource> pls;
2988 boost::shared_ptr<const Playlist> pl;
2989 vector<boost::shared_ptr<Region> > originals;
2990 vector<TwoRegions> old_and_new_regions;
2992 // (1) check that its really a compound region
2994 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
2998 pl = pls->playlist();
3000 framepos_t adjusted_start = 0; // gcc isn't smart enough
3001 framepos_t adjusted_end = 0; // gcc isn't smart enough
3003 /* the leftmost (earliest) edge of the compound region
3004 starts at zero in its source, or larger if it
3005 has been trimmed or content-scrolled.
3007 the rightmost (latest) edge of the compound region
3008 relative to its source is the starting point plus
3009 the length of the region.
3012 // (2) get all the original regions
3014 const RegionList& rl (pl->region_list().rlist());
3015 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3016 frameoffset_t move_offset = 0;
3018 /* there are two possibilities here:
3019 1) the playlist that the playlist source was based on
3020 is us, so just add the originals (which belonged to
3021 us anyway) back in the right place.
3023 2) the playlist that the playlist source was based on
3024 is NOT us, so we need to make copies of each of
3025 the original regions that we find, and add them
3028 bool same_playlist = (pls->original() == id());
3030 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3032 boost::shared_ptr<Region> current (*i);
3034 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3036 if (ca == cassocs.end()) {
3040 boost::shared_ptr<Region> original (ca->second);
3042 bool modified_region;
3044 if (i == rl.begin()) {
3045 move_offset = (target->position() - original->position()) - target->start();
3046 adjusted_start = original->position() + target->start();
3047 adjusted_end = adjusted_start + target->length();
3050 if (!same_playlist) {
3051 framepos_t pos = original->position();
3052 /* make a copy, but don't announce it */
3053 original = RegionFactory::create (original, false);
3054 /* the pure copy constructor resets position() to zero,
3057 original->set_position (pos);
3060 /* check to see how the original region (in the
3061 * playlist before compounding occured) overlaps
3062 * with the new state of the compound region.
3065 original->clear_changes ();
3066 modified_region = false;
3068 switch (original->coverage (adjusted_start, adjusted_end)) {
3069 case Evoral::OverlapNone:
3070 /* original region does not cover any part
3071 of the current state of the compound region
3075 case Evoral::OverlapInternal:
3076 /* overlap is just a small piece inside the
3077 * original so trim both ends
3079 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3080 modified_region = true;
3083 case Evoral::OverlapExternal:
3084 /* overlap fully covers original, so leave it
3089 case Evoral::OverlapEnd:
3090 /* overlap starts within but covers end,
3091 so trim the front of the region
3093 original->trim_front (adjusted_start);
3094 modified_region = true;
3097 case Evoral::OverlapStart:
3098 /* overlap covers start but ends within, so
3099 * trim the end of the region.
3101 original->trim_end (adjusted_end);
3102 modified_region = true;
3107 /* fix the position to match any movement of the compound region.
3109 original->set_position (original->position() + move_offset);
3110 modified_region = true;
3113 if (modified_region) {
3114 _session.add_command (new StatefulDiffCommand (original));
3117 /* and add to the list of regions waiting to be
3121 originals.push_back (original);
3122 old_and_new_regions.push_back (TwoRegions (*i, original));
3125 pre_uncombine (originals, target);
3127 in_partition = true;
3130 // (3) remove the compound region
3132 remove_region (target);
3134 // (4) add the constituent regions
3136 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3137 add_region ((*i), (*i)->position());
3138 set_layer((*i), (*i)->layer());
3139 if (!RegionFactory::region_by_id((*i)->id())) {
3140 RegionFactory::map_add(*i);
3144 in_partition = false;
3149 Playlist::fade_range (list<AudioRange>& ranges)
3151 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3152 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3153 (*i)->fade_range ((*r).start, (*r).end);
3159 Playlist::max_source_level () const
3161 RegionReadLock rlock (const_cast<Playlist *> (this));
3164 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3165 lvl = max (lvl, (*i)->max_source_level());
3172 Playlist::set_orig_track_id (const PBD::ID& id)
3174 _orig_track_id = id;
3177 /** Take a list of ranges, coalesce any that can be coalesced, then call
3178 * check_crossfades for each one.
3181 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3183 /* XXX: it's a shame that this coalesce algorithm also exists in
3184 TimeSelection::consolidate().
3187 /* XXX: xfade: this is implemented in Evoral::RangeList */
3190 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3191 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3197 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3198 i->from = min (i->from, j->from);
3199 i->to = max (i->to, j->to);
3208 Playlist::set_capture_insertion_in_progress (bool yn)
3210 _capture_insertion_underway = yn;