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 */
564 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
566 Playlist::flush_notifications (bool from_undo)
568 set<boost::shared_ptr<Region> >::iterator s;
569 bool regions_changed = false;
577 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
578 regions_changed = true;
581 /* XXX: it'd be nice if we could use pending_bounds for
582 RegionsExtended and RegionsMoved.
585 /* we have no idea what order the regions ended up in pending
586 bounds (it could be based on selection order, for example).
587 so, to preserve layering in the "most recently moved is higher"
588 model, sort them by existing layer, then timestamp them.
591 // RegionSortByLayer cmp;
592 // pending_bounds.sort (cmp);
594 list<Evoral::Range<framepos_t> > crossfade_ranges;
596 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
597 crossfade_ranges.push_back ((*r)->last_range ());
598 crossfade_ranges.push_back ((*r)->range ());
601 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
602 crossfade_ranges.push_back ((*s)->range ());
603 remove_dependents (*s);
604 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
607 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
608 crossfade_ranges.push_back ((*s)->range ());
609 /* don't emit RegionAdded signal until relayering is done,
610 so that the region is fully setup by the time
611 anyone hears that its been added
615 /* notify about contents/region changes first so that layering changes
616 * in a UI will take place on the new contents.
619 if (regions_changed || pending_contents_change) {
620 pending_layering = true;
621 ContentsChanged (); /* EMIT SIGNAL */
624 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
625 (*s)->clear_changes ();
626 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
629 if ((regions_changed && !in_set_state) || pending_layering) {
633 coalesce_and_check_crossfades (crossfade_ranges);
635 if (!pending_range_moves.empty ()) {
636 /* We don't need to check crossfades for these as pending_bounds has
639 RangesMoved (pending_range_moves, from_undo);
642 if (!pending_region_extensions.empty ()) {
643 RegionsExtended (pending_region_extensions);
652 Playlist::clear_pending ()
654 pending_adds.clear ();
655 pending_removes.clear ();
656 pending_bounds.clear ();
657 pending_range_moves.clear ();
658 pending_region_extensions.clear ();
659 pending_contents_change = false;
662 /*************************************************************
664 *************************************************************/
666 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
668 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
670 RegionWriteLock rlock (this);
671 times = fabs (times);
673 int itimes = (int) floor (times);
675 framepos_t pos = position;
677 if (times == 1 && auto_partition){
678 partition(pos - 1, (pos + region->length()), true);
682 add_region_internal (region, pos);
683 set_layer (region, DBL_MAX);
684 pos += region->length();
689 /* note that itimes can be zero if we being asked to just
690 insert a single fraction of the region.
693 for (int i = 0; i < itimes; ++i) {
694 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
695 add_region_internal (copy, pos);
696 set_layer (copy, DBL_MAX);
697 pos += region->length();
700 framecnt_t length = 0;
702 if (floor (times) != times) {
703 length = (framecnt_t) floor (region->length() * (times - floor (times)));
705 RegionFactory::region_name (name, region->name(), false);
710 plist.add (Properties::start, region->start());
711 plist.add (Properties::length, length);
712 plist.add (Properties::name, name);
713 plist.add (Properties::layer, region->layer());
715 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
716 add_region_internal (sub, pos);
717 set_layer (sub, DBL_MAX);
721 possibly_splice_unlocked (position, (pos + length) - position, region);
725 Playlist::set_region_ownership ()
727 RegionWriteLock rl (this);
728 RegionList::iterator i;
729 boost::weak_ptr<Playlist> pl (shared_from_this());
731 for (i = regions.begin(); i != regions.end(); ++i) {
732 (*i)->set_playlist (pl);
737 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
739 if (region->data_type() != _type) {
743 RegionSortByPosition cmp;
745 if (!first_set_state) {
746 boost::shared_ptr<Playlist> foo (shared_from_this());
747 region->set_playlist (boost::weak_ptr<Playlist>(foo));
750 region->set_position (position);
752 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
753 all_regions.insert (region);
755 possibly_splice_unlocked (position, region->length(), region);
757 if (!holding_state ()) {
758 /* layers get assigned from XML state, and are not reset during undo/redo */
762 /* we need to notify the existence of new region before checking dependents. Ick. */
764 notify_region_added (region);
766 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
772 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
774 RegionWriteLock rlock (this);
776 bool old_sp = _splicing;
779 remove_region_internal (old);
780 add_region_internal (newr, pos);
781 set_layer (newr, old->layer ());
785 possibly_splice_unlocked (pos, old->length() - newr->length());
789 Playlist::remove_region (boost::shared_ptr<Region> region)
791 RegionWriteLock rlock (this);
792 remove_region_internal (region);
796 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
798 RegionList::iterator i;
802 region->set_playlist (boost::weak_ptr<Playlist>());
805 /* XXX should probably freeze here .... */
807 for (i = regions.begin(); i != regions.end(); ++i) {
810 framepos_t pos = (*i)->position();
811 framecnt_t distance = (*i)->length();
815 possibly_splice_unlocked (pos, -distance);
817 if (!holding_state ()) {
819 remove_dependents (region);
822 notify_region_removed (region);
831 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
833 if (Config->get_use_overlap_equivalency()) {
834 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
835 if ((*i)->overlap_equivalent (other)) {
836 results.push_back (*i);
840 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
841 if ((*i)->equivalent (other)) {
842 results.push_back (*i);
849 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
851 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
853 if ((*i) && (*i)->region_list_equivalent (other)) {
854 results.push_back (*i);
860 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
862 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
864 if ((*i) && (*i)->any_source_equivalent (other)) {
865 results.push_back (*i);
871 Playlist::partition (framepos_t start, framepos_t end, bool cut)
875 partition_internal (start, end, cut, thawlist);
877 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
878 (*i)->resume_property_changes ();
882 /** Go through each region on the playlist and cut them at start and end, removing the section between
883 * start and end if cutting == true. Regions that lie entirely within start and end are always
888 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
890 RegionList new_regions;
893 RegionWriteLock rlock (this);
895 boost::shared_ptr<Region> region;
896 boost::shared_ptr<Region> current;
898 RegionList::iterator tmp;
899 Evoral::OverlapType overlap;
900 framepos_t pos1, pos2, pos3, pos4;
904 /* need to work from a copy, because otherwise the regions we add during the process
905 get operated on as well.
908 RegionList copy = regions.rlist();
910 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
917 if (current->first_frame() >= start && current->last_frame() < end) {
920 remove_region_internal (current);
926 /* coverage will return OverlapStart if the start coincides
927 with the end point. we do not partition such a region,
928 so catch this special case.
931 if (current->first_frame() >= end) {
935 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
939 pos1 = current->position();
942 pos4 = current->last_frame();
944 if (overlap == Evoral::OverlapInternal) {
945 /* split: we need 3 new regions, the front, middle and end.
946 cut: we need 2 regions, the front and end.
951 ---------------*************************------------
954 ---------------*****++++++++++++++++====------------
956 ---------------*****----------------====------------
961 /* "middle" ++++++ */
963 RegionFactory::region_name (new_name, current->name(), false);
967 plist.add (Properties::start, current->start() + (pos2 - pos1));
968 plist.add (Properties::length, pos3 - pos2);
969 plist.add (Properties::name, new_name);
970 plist.add (Properties::layer, current->layer ());
971 plist.add (Properties::layering_index, current->layering_index ());
972 plist.add (Properties::automatic, true);
973 plist.add (Properties::left_of_split, true);
974 plist.add (Properties::right_of_split, true);
976 region = RegionFactory::create (current, plist);
977 add_region_internal (region, start);
978 new_regions.push_back (region);
983 RegionFactory::region_name (new_name, current->name(), false);
987 plist.add (Properties::start, current->start() + (pos3 - pos1));
988 plist.add (Properties::length, pos4 - pos3);
989 plist.add (Properties::name, new_name);
990 plist.add (Properties::layer, current->layer ());
991 plist.add (Properties::layering_index, current->layering_index ());
992 plist.add (Properties::automatic, true);
993 plist.add (Properties::right_of_split, true);
995 region = RegionFactory::create (current, plist);
997 add_region_internal (region, end);
998 new_regions.push_back (region);
1002 current->suspend_property_changes ();
1003 thawlist.push_back (current);
1004 current->cut_end (pos2 - 1);
1006 } else if (overlap == Evoral::OverlapEnd) {
1010 ---------------*************************------------
1013 ---------------**************+++++++++++------------
1015 ---------------**************-----------------------
1022 RegionFactory::region_name (new_name, current->name(), false);
1026 plist.add (Properties::start, current->start() + (pos2 - pos1));
1027 plist.add (Properties::length, pos4 - pos2);
1028 plist.add (Properties::name, new_name);
1029 plist.add (Properties::layer, current->layer ());
1030 plist.add (Properties::layering_index, current->layering_index ());
1031 plist.add (Properties::automatic, true);
1032 plist.add (Properties::left_of_split, true);
1034 region = RegionFactory::create (current, plist);
1036 add_region_internal (region, start);
1037 new_regions.push_back (region);
1042 current->suspend_property_changes ();
1043 thawlist.push_back (current);
1044 current->cut_end (pos2 - 1);
1046 } else if (overlap == Evoral::OverlapStart) {
1048 /* split: we need 2 regions: the front and the end.
1049 cut: just trim current to skip the cut area
1054 ---------------*************************------------
1058 ---------------****+++++++++++++++++++++------------
1060 -------------------*********************------------
1066 RegionFactory::region_name (new_name, current->name(), false);
1070 plist.add (Properties::start, current->start());
1071 plist.add (Properties::length, pos3 - pos1);
1072 plist.add (Properties::name, new_name);
1073 plist.add (Properties::layer, current->layer ());
1074 plist.add (Properties::layering_index, current->layering_index ());
1075 plist.add (Properties::automatic, true);
1076 plist.add (Properties::right_of_split, true);
1078 region = RegionFactory::create (current, plist);
1080 add_region_internal (region, pos1);
1081 new_regions.push_back (region);
1086 current->suspend_property_changes ();
1087 thawlist.push_back (current);
1088 current->trim_front (pos3);
1089 } else if (overlap == Evoral::OverlapExternal) {
1091 /* split: no split required.
1092 cut: remove the region.
1097 ---------------*************************------------
1101 ---------------*************************------------
1103 ----------------------------------------------------
1108 remove_region_internal (current);
1111 new_regions.push_back (current);
1115 in_partition = false;
1118 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1119 framepos_t wanted_length = end-start;
1120 _end_space = wanted_length - get_extent().second-get_extent().first;
1123 boost::shared_ptr<Playlist>
1124 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1126 boost::shared_ptr<Playlist> ret;
1127 boost::shared_ptr<Playlist> pl;
1130 if (ranges.empty()) {
1131 return boost::shared_ptr<Playlist>();
1134 start = ranges.front().start;
1136 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1138 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1140 if (i == ranges.begin()) {
1144 /* paste the next section into the nascent playlist,
1145 offset to reflect the start of the first range we
1149 ret->paste (pl, (*i).start - start, 1.0f);
1156 boost::shared_ptr<Playlist>
1157 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1159 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1160 return cut_copy (pmf, ranges, result_is_hidden);
1163 boost::shared_ptr<Playlist>
1164 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1166 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1167 return cut_copy (pmf, ranges, result_is_hidden);
1170 boost::shared_ptr<Playlist>
1171 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1173 boost::shared_ptr<Playlist> the_copy;
1174 RegionList thawlist;
1177 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1178 string new_name = _name;
1182 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1183 return boost::shared_ptr<Playlist>();
1186 partition_internal (start, start+cnt-1, true, thawlist);
1188 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1189 (*i)->resume_property_changes();
1195 boost::shared_ptr<Playlist>
1196 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1200 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1201 string new_name = _name;
1205 // 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... )
1207 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1211 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1213 times = fabs (times);
1216 RegionReadLock rl2 (other.get());
1218 int itimes = (int) floor (times);
1219 framepos_t pos = position;
1220 framecnt_t const shift = other->_get_extent().second;
1221 layer_t top = top_layer ();
1224 RegionWriteLock rl1 (this);
1226 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1227 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1229 /* put these new regions on top of all existing ones, but preserve
1230 the ordering they had in the original playlist.
1233 add_region_internal (copy_of_region, (*i)->position() + pos);
1234 set_layer (copy_of_region, copy_of_region->layer() + top);
1246 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1248 times = fabs (times);
1250 RegionWriteLock rl (this);
1251 int itimes = (int) floor (times);
1252 framepos_t pos = position + 1;
1255 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1256 add_region_internal (copy, pos);
1257 set_layer (copy, DBL_MAX);
1258 pos += region->length();
1261 if (floor (times) != times) {
1262 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1264 RegionFactory::region_name (name, region->name(), false);
1269 plist.add (Properties::start, region->start());
1270 plist.add (Properties::length, length);
1271 plist.add (Properties::name, name);
1273 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1274 add_region_internal (sub, pos);
1275 set_layer (sub, DBL_MAX);
1281 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1283 RegionWriteLock rlock (this);
1284 RegionList copy (regions.rlist());
1287 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1289 if ((*r)->last_frame() < at) {
1294 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1295 /* intersected region */
1296 if (!move_intersected) {
1301 /* do not move regions glued to music time - that
1302 has to be done separately.
1305 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1306 fixup.push_back (*r);
1310 (*r)->set_position ((*r)->position() + distance);
1313 /* XXX: may not be necessary; Region::post_set should do this, I think */
1314 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1315 (*r)->recompute_position_from_lock_style ();
1320 Playlist::split (framepos_t at)
1322 RegionWriteLock rlock (this);
1323 RegionList copy (regions.rlist());
1325 /* use a copy since this operation can modify the region list
1328 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1329 _split_region (*r, at);
1334 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1336 RegionWriteLock rl (this);
1337 _split_region (region, playlist_position);
1341 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1343 if (!region->covers (playlist_position)) {
1347 if (region->position() == playlist_position ||
1348 region->last_frame() == playlist_position) {
1352 boost::shared_ptr<Region> left;
1353 boost::shared_ptr<Region> right;
1354 frameoffset_t before;
1355 frameoffset_t after;
1359 /* split doesn't change anything about length, so don't try to splice */
1361 bool old_sp = _splicing;
1364 before = playlist_position - region->position();
1365 after = region->length() - before;
1367 RegionFactory::region_name (before_name, region->name(), false);
1372 plist.add (Properties::position, region->position ());
1373 plist.add (Properties::length, before);
1374 plist.add (Properties::name, before_name);
1375 plist.add (Properties::left_of_split, true);
1376 plist.add (Properties::layering_index, region->layering_index ());
1377 plist.add (Properties::layer, region->layer ());
1379 /* note: we must use the version of ::create with an offset here,
1380 since it supplies that offset to the Region constructor, which
1381 is necessary to get audio region gain envelopes right.
1383 left = RegionFactory::create (region, 0, plist);
1386 RegionFactory::region_name (after_name, region->name(), false);
1391 plist.add (Properties::position, region->position() + before);
1392 plist.add (Properties::length, after);
1393 plist.add (Properties::name, after_name);
1394 plist.add (Properties::right_of_split, true);
1395 plist.add (Properties::layering_index, region->layering_index ());
1396 plist.add (Properties::layer, region->layer ());
1398 /* same note as above */
1399 right = RegionFactory::create (region, before, plist);
1402 add_region_internal (left, region->position());
1403 add_region_internal (right, region->position() + before);
1404 remove_region_internal (region);
1410 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1412 if (_splicing || in_set_state) {
1413 /* don't respond to splicing moves or state setting */
1417 if (_edit_mode == Splice) {
1418 splice_locked (at, distance, exclude);
1423 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1425 if (_splicing || in_set_state) {
1426 /* don't respond to splicing moves or state setting */
1430 if (_edit_mode == Splice) {
1431 splice_unlocked (at, distance, exclude);
1436 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1439 RegionWriteLock rl (this);
1440 core_splice (at, distance, exclude);
1445 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1447 core_splice (at, distance, exclude);
1451 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1455 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1457 if (exclude && (*i) == exclude) {
1461 if ((*i)->position() >= at) {
1462 framepos_t new_pos = (*i)->position() + distance;
1465 } else if (new_pos >= max_framepos - (*i)->length()) {
1466 new_pos = max_framepos - (*i)->length();
1469 (*i)->set_position (new_pos);
1475 notify_contents_changed ();
1479 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1482 RegionWriteLock rl (this);
1483 core_ripple (at, distance, exclude);
1488 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1490 core_ripple (at, distance, exclude);
1494 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1496 if (distance == 0) {
1501 RegionListProperty copy = regions;
1502 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1503 assert (i != copy.end());
1506 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1511 if ((*i)->position() >= at) {
1512 framepos_t new_pos = (*i)->position() + distance;
1513 framepos_t limit = max_framepos - (*i)->length();
1516 } else if (new_pos >= limit ) {
1520 (*i)->set_position (new_pos);
1525 notify_contents_changed ();
1530 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1532 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1536 if (what_changed.contains (Properties::position)) {
1538 /* remove it from the list then add it back in
1539 the right place again.
1542 RegionSortByPosition cmp;
1544 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1546 if (i == regions.end()) {
1547 /* the region bounds are being modified but its not currently
1548 in the region list. we will use its bounds correctly when/if
1555 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1558 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1560 frameoffset_t delta = 0;
1562 if (what_changed.contains (Properties::position)) {
1563 delta = region->position() - region->last_position();
1566 if (what_changed.contains (Properties::length)) {
1567 delta += region->length() - region->last_length();
1571 possibly_splice (region->last_position() + region->last_length(), delta, region);
1574 if (holding_state ()) {
1575 pending_bounds.push_back (region);
1577 notify_contents_changed ();
1579 list<Evoral::Range<framepos_t> > xf;
1580 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1581 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1582 coalesce_and_check_crossfades (xf);
1588 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1590 boost::shared_ptr<Region> region (weak_region.lock());
1596 /* this makes a virtual call to the right kind of playlist ... */
1598 region_changed (what_changed, region);
1602 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1604 PropertyChange our_interests;
1605 PropertyChange bounds;
1606 PropertyChange pos_and_length;
1609 if (in_set_state || in_flush) {
1613 our_interests.add (Properties::muted);
1614 our_interests.add (Properties::layer);
1615 our_interests.add (Properties::opaque);
1617 bounds.add (Properties::start);
1618 bounds.add (Properties::position);
1619 bounds.add (Properties::length);
1621 pos_and_length.add (Properties::position);
1622 pos_and_length.add (Properties::length);
1624 if (what_changed.contains (bounds)) {
1625 region_bounds_changed (what_changed, region);
1626 save = !(_splicing || _nudging);
1629 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1630 notify_region_moved (region);
1631 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1632 notify_region_end_trimmed (region);
1633 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1634 notify_region_start_trimmed (region);
1637 /* don't notify about layer changes, since we are the only object that can initiate
1638 them, and we notify in ::relayer()
1641 if (what_changed.contains (our_interests)) {
1645 mark_session_dirty ();
1651 Playlist::drop_regions ()
1653 RegionWriteLock rl (this);
1655 all_regions.clear ();
1659 Playlist::sync_all_regions_with_regions ()
1661 RegionWriteLock rl (this);
1663 all_regions.clear ();
1665 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1666 all_regions.insert (*i);
1671 Playlist::clear (bool with_signals)
1674 RegionWriteLock rl (this);
1676 region_state_changed_connections.drop_connections ();
1678 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1679 pending_removes.insert (*i);
1684 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1685 remove_dependents (*s);
1691 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1692 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1695 pending_removes.clear ();
1696 pending_contents_change = false;
1702 /***********************************************************************
1704 **********************************************************************/
1706 boost::shared_ptr<RegionList>
1707 Playlist::regions_at (framepos_t frame)
1709 RegionReadLock rlock (this);
1710 return find_regions_at (frame);
1714 Playlist::count_regions_at (framepos_t frame) const
1716 RegionReadLock rlock (const_cast<Playlist*>(this));
1719 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1720 if ((*i)->covers (frame)) {
1728 boost::shared_ptr<Region>
1729 Playlist::top_region_at (framepos_t frame)
1732 RegionReadLock rlock (this);
1733 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1734 boost::shared_ptr<Region> region;
1736 if (rlist->size()) {
1737 RegionSortByLayer cmp;
1739 region = rlist->back();
1745 boost::shared_ptr<Region>
1746 Playlist::top_unmuted_region_at (framepos_t frame)
1749 RegionReadLock rlock (this);
1750 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1752 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1754 RegionList::iterator tmp = i;
1757 if ((*i)->muted()) {
1764 boost::shared_ptr<Region> region;
1766 if (rlist->size()) {
1767 RegionSortByLayer cmp;
1769 region = rlist->back();
1775 boost::shared_ptr<RegionList>
1776 Playlist::find_regions_at (framepos_t frame)
1778 /* Caller must hold lock */
1780 boost::shared_ptr<RegionList> rlist (new RegionList);
1782 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1783 if ((*i)->covers (frame)) {
1784 rlist->push_back (*i);
1791 boost::shared_ptr<RegionList>
1792 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1794 RegionReadLock rlock (this);
1795 boost::shared_ptr<RegionList> rlist (new RegionList);
1797 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1798 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1799 rlist->push_back (*i);
1806 boost::shared_ptr<RegionList>
1807 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1809 RegionReadLock rlock (this);
1810 boost::shared_ptr<RegionList> rlist (new RegionList);
1812 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1813 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1814 rlist->push_back (*i);
1821 /** @param start Range start.
1822 * @param end Range end.
1823 * @return regions which have some part within this range.
1825 boost::shared_ptr<RegionList>
1826 Playlist::regions_touched (framepos_t start, framepos_t end)
1828 RegionReadLock rlock (this);
1829 return regions_touched_locked (start, end);
1832 boost::shared_ptr<RegionList>
1833 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1835 boost::shared_ptr<RegionList> rlist (new RegionList);
1837 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1838 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1839 rlist->push_back (*i);
1847 Playlist::find_next_transient (framepos_t from, int dir)
1849 RegionReadLock rlock (this);
1850 AnalysisFeatureList points;
1851 AnalysisFeatureList these_points;
1853 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1855 if ((*i)->last_frame() < from) {
1859 if ((*i)->first_frame() > from) {
1864 (*i)->get_transients (these_points);
1866 /* add first frame, just, err, because */
1868 these_points.push_back ((*i)->first_frame());
1870 points.insert (points.end(), these_points.begin(), these_points.end());
1871 these_points.clear ();
1874 if (points.empty()) {
1878 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1879 bool reached = false;
1882 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1887 if (reached && (*x) > from) {
1892 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1897 if (reached && (*x) < from) {
1906 boost::shared_ptr<Region>
1907 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1909 RegionReadLock rlock (this);
1910 boost::shared_ptr<Region> ret;
1911 framepos_t closest = max_framepos;
1913 bool end_iter = false;
1915 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1919 frameoffset_t distance;
1920 boost::shared_ptr<Region> r = (*i);
1925 pos = r->first_frame ();
1928 pos = r->last_frame ();
1931 pos = r->sync_position ();
1936 case 1: /* forwards */
1939 if ((distance = pos - frame) < closest) {
1948 default: /* backwards */
1951 if ((distance = frame - pos) < closest) {
1967 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1969 RegionReadLock rlock (this);
1971 framepos_t closest = max_framepos;
1972 framepos_t ret = -1;
1976 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1978 boost::shared_ptr<Region> r = (*i);
1979 frameoffset_t distance;
1981 if (r->first_frame() > frame) {
1983 distance = r->first_frame() - frame;
1985 if (distance < closest) {
1986 ret = r->first_frame();
1991 if (r->last_frame () > frame) {
1993 distance = r->last_frame () - frame;
1995 if (distance < closest) {
1996 ret = r->last_frame ();
2004 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2006 boost::shared_ptr<Region> r = (*i);
2007 frameoffset_t distance;
2009 if (r->last_frame() < frame) {
2011 distance = frame - r->last_frame();
2013 if (distance < closest) {
2014 ret = r->last_frame();
2019 if (r->first_frame() < frame) {
2021 distance = frame - r->first_frame();
2023 if (distance < closest) {
2024 ret = r->first_frame();
2035 /***********************************************************************/
2041 Playlist::mark_session_dirty ()
2043 if (!in_set_state && !holding_state ()) {
2044 _session.set_dirty();
2049 Playlist::rdiff (vector<Command*>& cmds) const
2051 RegionReadLock rlock (const_cast<Playlist *> (this));
2052 Stateful::rdiff (cmds);
2056 Playlist::clear_owned_changes ()
2058 RegionReadLock rlock (this);
2059 Stateful::clear_owned_changes ();
2063 Playlist::update (const RegionListProperty::ChangeRecord& change)
2065 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2066 name(), change.added.size(), change.removed.size()));
2069 /* add the added regions */
2070 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2071 add_region_internal ((*i), (*i)->position());
2073 /* remove the removed regions */
2074 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2082 Playlist::set_state (const XMLNode& node, int version)
2086 XMLNodeConstIterator niter;
2087 XMLPropertyList plist;
2088 XMLPropertyConstIterator piter;
2090 boost::shared_ptr<Region> region;
2092 bool seen_region_nodes = false;
2097 if (node.name() != "Playlist") {
2104 plist = node.properties();
2108 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2112 if (prop->name() == X_("name")) {
2113 _name = prop->value();
2115 } else if (prop->name() == X_("orig-diskstream-id")) {
2116 /* XXX legacy session: fix up later */
2117 _orig_track_id = prop->value ();
2118 } else if (prop->name() == X_("orig-track-id")) {
2119 _orig_track_id = prop->value ();
2120 } else if (prop->name() == X_("frozen")) {
2121 _frozen = string_is_affirmative (prop->value());
2122 } else if (prop->name() == X_("combine-ops")) {
2123 _combine_ops = atoi (prop->value());
2129 nlist = node.children();
2131 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2135 if (child->name() == "Region") {
2137 seen_region_nodes = true;
2139 if ((prop = child->property ("id")) == 0) {
2140 error << _("region state node has no ID, ignored") << endmsg;
2144 ID id = prop->value ();
2146 if ((region = region_by_id (id))) {
2148 region->suspend_property_changes ();
2150 if (region->set_state (*child, version)) {
2151 region->resume_property_changes ();
2155 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2156 region->suspend_property_changes ();
2158 error << _("Playlist: cannot create region from XML") << endmsg;
2163 RegionWriteLock rlock (this);
2164 add_region_internal (region, region->position());
2167 region->resume_property_changes ();
2172 if (seen_region_nodes && regions.empty()) {
2177 notify_contents_changed ();
2180 first_set_state = false;
2186 Playlist::get_state()
2188 return state (true);
2192 Playlist::get_template()
2194 return state (false);
2197 /** @param full_state true to include regions in the returned state, otherwise false.
2200 Playlist::state (bool full_state)
2202 XMLNode *node = new XMLNode (X_("Playlist"));
2205 node->add_property (X_("id"), id().to_s());
2206 node->add_property (X_("name"), _name);
2207 node->add_property (X_("type"), _type.to_string());
2209 _orig_track_id.print (buf, sizeof (buf));
2210 node->add_property (X_("orig-track-id"), buf);
2211 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2214 RegionReadLock rlock (this);
2216 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2217 node->add_property ("combine-ops", buf);
2219 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2220 node->add_child_nocopy ((*i)->get_state());
2225 node->add_child_copy (*_extra_xml);
2232 Playlist::empty() const
2234 RegionReadLock rlock (const_cast<Playlist *>(this));
2235 return regions.empty();
2239 Playlist::n_regions() const
2241 RegionReadLock rlock (const_cast<Playlist *>(this));
2242 return regions.size();
2245 /** @return true if the all_regions list is empty, ie this playlist
2246 * has never had a region added to it.
2249 Playlist::all_regions_empty() const
2251 RegionReadLock rl (const_cast<Playlist *> (this));
2252 return all_regions.empty();
2255 pair<framepos_t, framepos_t>
2256 Playlist::get_extent () const
2258 RegionReadLock rlock (const_cast<Playlist *>(this));
2259 return _get_extent ();
2262 pair<framepos_t, framepos_t>
2263 Playlist::get_extent_with_endspace () const
2265 pair<framepos_t, framepos_t> l = get_extent();
2266 l.second += _end_space;
2270 pair<framepos_t, framepos_t>
2271 Playlist::_get_extent () const
2273 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2275 if (regions.empty()) {
2280 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2281 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2282 if (e.first < ext.first) {
2283 ext.first = e.first;
2285 if (e.second > ext.second) {
2286 ext.second = e.second;
2294 Playlist::bump_name (string name, Session &session)
2296 string newname = name;
2299 newname = bump_name_once (newname, '.');
2300 } while (session.playlists->by_name (newname)!=NULL);
2307 Playlist::top_layer() const
2309 RegionReadLock rlock (const_cast<Playlist *> (this));
2312 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2313 top = max (top, (*i)->layer());
2319 Playlist::set_edit_mode (EditMode mode)
2324 struct RelayerSort {
2325 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2326 return a->layering_index() < b->layering_index();
2330 /** Set a new layer for a region. This adjusts the layering indices of all
2331 * regions in the playlist to put the specified region in the appropriate
2332 * place. The actual layering will be fixed up when relayer() happens.
2336 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2338 /* Remove the layer we are setting from our region list, and sort it
2339 * using the layer indeces.
2342 RegionList copy = regions.rlist();
2343 copy.remove (region);
2344 copy.sort (RelayerSort ());
2346 /* Put region back in the right place */
2347 RegionList::iterator i = copy.begin();
2348 while (i != copy.end ()) {
2349 if ((*i)->layer() > new_layer) {
2355 copy.insert (i, region);
2357 setup_layering_indices (copy);
2361 Playlist::setup_layering_indices (RegionList const & regions)
2365 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2366 (*k)->set_layering_index (j++);
2370 struct LaterHigherSort {
2371 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2372 return a->position() < b->position();
2376 /** Take the layering indices of each of our regions, compute the layers
2377 * that they should be on, and write the layers back to the regions.
2380 Playlist::relayer ()
2382 /* never compute layers when setting from XML */
2388 /* Build up a new list of regions on each layer, stored in a set of lists
2389 each of which represent some period of time on some layer. The idea
2390 is to avoid having to search the entire region list to establish whether
2391 each region overlaps another */
2393 /* how many pieces to divide this playlist's time up into */
2394 int const divisions = 512;
2396 /* find the start and end positions of the regions on this playlist */
2397 framepos_t start = INT64_MAX;
2399 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2400 start = min (start, (*i)->position());
2401 end = max (end, (*i)->position() + (*i)->length());
2404 /* hence the size of each time division */
2405 double const division_size = (end - start) / double (divisions);
2407 vector<vector<RegionList> > layers;
2408 layers.push_back (vector<RegionList> (divisions));
2410 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2411 RegionList copy = regions.rlist();
2412 switch (Config->get_layer_model()) {
2414 copy.sort (LaterHigherSort ());
2417 copy.sort (RelayerSort ());
2421 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2422 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2423 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2426 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2428 /* find the time divisions that this region covers; if there are no regions on the list,
2429 division_size will equal 0 and in this case we'll just say that
2430 start_division = end_division = 0.
2432 int start_division = 0;
2433 int end_division = 0;
2435 if (division_size > 0) {
2436 start_division = floor ( ((*i)->position() - start) / division_size);
2437 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2438 if (end_division == divisions) {
2443 assert (divisions == 0 || end_division < divisions);
2445 /* find the lowest layer that this region can go on */
2446 size_t j = layers.size();
2448 /* try layer j - 1; it can go on if it overlaps no other region
2449 that is already on that layer
2452 bool overlap = false;
2453 for (int k = start_division; k <= end_division; ++k) {
2454 RegionList::iterator l = layers[j-1][k].begin ();
2455 while (l != layers[j-1][k].end()) {
2456 if ((*l)->overlap_equivalent (*i)) {
2469 /* overlap, so we must use layer j */
2476 if (j == layers.size()) {
2477 /* we need a new layer for this region */
2478 layers.push_back (vector<RegionList> (divisions));
2481 /* put a reference to this region in each of the divisions that it exists in */
2482 for (int k = start_division; k <= end_division; ++k) {
2483 layers[j][k].push_back (*i);
2486 (*i)->set_layer (j);
2489 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2490 relayering because we just removed the only region on the top layer, nothing will
2491 appear to have changed, but the StreamView must still sort itself out. We could
2492 probably keep a note of the top layer last time we relayered, and check that,
2493 but premature optimisation &c...
2495 notify_layering_changed ();
2497 /* This relayer() may have been called as a result of a region removal, in which
2498 case we need to setup layering indices to account for the one that has just
2501 setup_layering_indices (copy);
2505 Playlist::raise_region (boost::shared_ptr<Region> region)
2507 set_layer (region, region->layer() + 1.5);
2512 Playlist::lower_region (boost::shared_ptr<Region> region)
2514 set_layer (region, region->layer() - 1.5);
2519 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2521 set_layer (region, DBL_MAX);
2526 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2528 set_layer (region, -0.5);
2533 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2535 RegionList::iterator i;
2541 RegionWriteLock rlock (const_cast<Playlist *> (this));
2543 for (i = regions.begin(); i != regions.end(); ++i) {
2545 if ((*i)->position() >= start) {
2551 if ((*i)->last_frame() > max_framepos - distance) {
2552 new_pos = max_framepos - (*i)->length();
2554 new_pos = (*i)->position() + distance;
2559 if ((*i)->position() > distance) {
2560 new_pos = (*i)->position() - distance;
2566 (*i)->set_position (new_pos);
2574 notify_contents_changed ();
2580 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2582 RegionReadLock rlock (const_cast<Playlist*> (this));
2584 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2585 if ((*r)->uses_source (src)) {
2593 boost::shared_ptr<Region>
2594 Playlist::find_region (const ID& id) const
2596 RegionReadLock rlock (const_cast<Playlist*> (this));
2598 /* searches all regions currently in use by the playlist */
2600 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2601 if ((*i)->id() == id) {
2606 return boost::shared_ptr<Region> ();
2610 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2612 RegionReadLock rlock (const_cast<Playlist*> (this));
2615 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2621 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2622 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2623 /* check if region is used in a compound */
2624 if (it->second == r) {
2625 /* region is referenced as 'original' of a compound */
2629 if (r->whole_file() && r->max_source_level() > 0) {
2630 /* region itself ia a compound.
2631 * the compound regions are not referenced -> check regions inside compound
2633 const SourceList& sl = r->sources();
2634 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2635 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2637 if (ps->playlist()->region_use_count(it->first)) {
2638 // break out of both loops
2647 boost::shared_ptr<Region>
2648 Playlist::region_by_id (const ID& id) const
2650 /* searches all regions ever added to this playlist */
2652 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2653 if ((*i)->id() == id) {
2657 return boost::shared_ptr<Region> ();
2661 Playlist::dump () const
2663 boost::shared_ptr<Region> r;
2665 cerr << "Playlist \"" << _name << "\" " << endl
2666 << regions.size() << " regions "
2669 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2671 cerr << " " << r->name() << " ["
2672 << r->start() << "+" << r->length()
2682 Playlist::set_frozen (bool yn)
2688 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2692 if (region->locked()) {
2699 RegionWriteLock rlock (const_cast<Playlist*> (this));
2704 RegionList::iterator next;
2706 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2707 if ((*i) == region) {
2711 if (next != regions.end()) {
2713 if ((*next)->locked()) {
2719 if ((*next)->position() != region->last_frame() + 1) {
2720 /* they didn't used to touch, so after shuffle,
2721 just have them swap positions.
2723 new_pos = (*next)->position();
2725 /* they used to touch, so after shuffle,
2726 make sure they still do. put the earlier
2727 region where the later one will end after
2730 new_pos = region->position() + (*next)->length();
2733 (*next)->set_position (region->position());
2734 region->set_position (new_pos);
2736 /* avoid a full sort */
2738 regions.erase (i); // removes the region from the list */
2740 regions.insert (next, region); // adds it back after next
2749 RegionList::iterator prev = regions.end();
2751 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2752 if ((*i) == region) {
2754 if (prev != regions.end()) {
2756 if ((*prev)->locked()) {
2761 if (region->position() != (*prev)->last_frame() + 1) {
2762 /* they didn't used to touch, so after shuffle,
2763 just have them swap positions.
2765 new_pos = region->position();
2767 /* they used to touch, so after shuffle,
2768 make sure they still do. put the earlier
2769 one where the later one will end after
2771 new_pos = (*prev)->position() + region->length();
2774 region->set_position ((*prev)->position());
2775 (*prev)->set_position (new_pos);
2777 /* avoid a full sort */
2779 regions.erase (i); // remove region
2780 regions.insert (prev, region); // insert region before prev
2796 notify_contents_changed();
2802 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2804 RegionReadLock rlock (const_cast<Playlist*> (this));
2806 if (regions.size() > 1) {
2814 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2816 ripple_locked (at, distance, exclude);
2820 Playlist::update_after_tempo_map_change ()
2822 RegionWriteLock rlock (const_cast<Playlist*> (this));
2823 RegionList copy (regions.rlist());
2827 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2828 (*i)->update_after_tempo_map_change ();
2835 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2837 RegionWriteLock rl (this, false);
2838 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2844 Playlist::has_region_at (framepos_t const p) const
2846 RegionReadLock (const_cast<Playlist *> (this));
2848 RegionList::const_iterator i = regions.begin ();
2849 while (i != regions.end() && !(*i)->covers (p)) {
2853 return (i != regions.end());
2856 /** Remove any region that uses a given source */
2858 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2860 RegionWriteLock rl (this);
2862 RegionList::iterator i = regions.begin();
2863 while (i != regions.end()) {
2864 RegionList::iterator j = i;
2867 if ((*i)->uses_source (s)) {
2868 remove_region_internal (*i);
2875 /** Look from a session frame time and find the start time of the next region
2876 * which is on the top layer of this playlist.
2877 * @param t Time to look from.
2878 * @return Position of next top-layered region, or max_framepos if there isn't one.
2881 Playlist::find_next_top_layer_position (framepos_t t) const
2883 RegionReadLock rlock (const_cast<Playlist *> (this));
2885 layer_t const top = top_layer ();
2887 RegionList copy = regions.rlist ();
2888 copy.sort (RegionSortByPosition ());
2890 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2891 if ((*i)->position() >= t && (*i)->layer() == top) {
2892 return (*i)->position();
2896 return max_framepos;
2899 boost::shared_ptr<Region>
2900 Playlist::combine (const RegionList& r)
2903 uint32_t channels = 0;
2905 framepos_t earliest_position = max_framepos;
2906 vector<TwoRegions> old_and_new_regions;
2907 vector<boost::shared_ptr<Region> > originals;
2908 vector<boost::shared_ptr<Region> > copies;
2911 uint32_t max_level = 0;
2913 /* find the maximum depth of all the regions we're combining */
2915 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2916 max_level = max (max_level, (*i)->max_source_level());
2919 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2920 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2922 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2924 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2925 earliest_position = min (earliest_position, (*i)->position());
2928 /* enable this so that we do not try to create xfades etc. as we add
2932 pl->in_partition = true;
2934 /* sort by position then layer.
2935 * route_time_axis passes 'selected_regions' - which is not sorted.
2936 * here we need the top-most first, then every layer's region sorted by position.
2938 RegionList sorted(r);
2939 sorted.sort(RegionSortByLayerAndPosition());
2941 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
2943 /* copy the region */
2945 boost::shared_ptr<Region> original_region = (*i);
2946 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2948 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2949 originals.push_back (original_region);
2950 copies.push_back (copied_region);
2952 RegionFactory::add_compound_association (original_region, copied_region);
2954 /* make position relative to zero */
2956 pl->add_region (copied_region, original_region->position() - earliest_position);
2957 copied_region->set_layer (original_region->layer ());
2959 /* use the maximum number of channels for any region */
2961 channels = max (channels, original_region->n_channels());
2963 /* it will go above the layer of the highest existing region */
2965 layer = max (layer, original_region->layer());
2968 pl->in_partition = false;
2970 pre_combine (copies);
2972 /* now create a new PlaylistSource for each channel in the new playlist */
2975 pair<framepos_t,framepos_t> extent = pl->get_extent();
2977 for (uint32_t chn = 0; chn < channels; ++chn) {
2978 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2982 /* now a new whole-file region using the list of sources */
2984 plist.add (Properties::start, 0);
2985 plist.add (Properties::length, extent.second);
2986 plist.add (Properties::name, parent_name);
2987 plist.add (Properties::whole_file, true);
2989 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2991 /* now the non-whole-file region that we will actually use in the
2996 plist.add (Properties::start, 0);
2997 plist.add (Properties::length, extent.second);
2998 plist.add (Properties::name, child_name);
2999 plist.add (Properties::layer, layer+1);
3001 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3003 /* remove all the selected regions from the current playlist
3008 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3012 /* do type-specific stuff with the originals and the new compound
3016 post_combine (originals, compound_region);
3018 /* add the new region at the right location */
3020 add_region (compound_region, earliest_position);
3026 return compound_region;
3030 Playlist::uncombine (boost::shared_ptr<Region> target)
3032 boost::shared_ptr<PlaylistSource> pls;
3033 boost::shared_ptr<const Playlist> pl;
3034 vector<boost::shared_ptr<Region> > originals;
3035 vector<TwoRegions> old_and_new_regions;
3037 // (1) check that its really a compound region
3039 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3043 pl = pls->playlist();
3045 framepos_t adjusted_start = 0; // gcc isn't smart enough
3046 framepos_t adjusted_end = 0; // gcc isn't smart enough
3048 /* the leftmost (earliest) edge of the compound region
3049 starts at zero in its source, or larger if it
3050 has been trimmed or content-scrolled.
3052 the rightmost (latest) edge of the compound region
3053 relative to its source is the starting point plus
3054 the length of the region.
3057 // (2) get all the original regions
3059 const RegionList& rl (pl->region_list().rlist());
3060 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3061 frameoffset_t move_offset = 0;
3063 /* there are two possibilities here:
3064 1) the playlist that the playlist source was based on
3065 is us, so just add the originals (which belonged to
3066 us anyway) back in the right place.
3068 2) the playlist that the playlist source was based on
3069 is NOT us, so we need to make copies of each of
3070 the original regions that we find, and add them
3073 bool same_playlist = (pls->original() == id());
3075 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3077 boost::shared_ptr<Region> current (*i);
3079 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3081 if (ca == cassocs.end()) {
3085 boost::shared_ptr<Region> original (ca->second);
3087 bool modified_region;
3089 if (i == rl.begin()) {
3090 move_offset = (target->position() - original->position()) - target->start();
3091 adjusted_start = original->position() + target->start();
3092 adjusted_end = adjusted_start + target->length();
3095 if (!same_playlist) {
3096 framepos_t pos = original->position();
3097 /* make a copy, but don't announce it */
3098 original = RegionFactory::create (original, false);
3099 /* the pure copy constructor resets position() to zero,
3102 original->set_position (pos);
3105 /* check to see how the original region (in the
3106 * playlist before compounding occured) overlaps
3107 * with the new state of the compound region.
3110 original->clear_changes ();
3111 modified_region = false;
3113 switch (original->coverage (adjusted_start, adjusted_end)) {
3114 case Evoral::OverlapNone:
3115 /* original region does not cover any part
3116 of the current state of the compound region
3120 case Evoral::OverlapInternal:
3121 /* overlap is just a small piece inside the
3122 * original so trim both ends
3124 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3125 modified_region = true;
3128 case Evoral::OverlapExternal:
3129 /* overlap fully covers original, so leave it
3134 case Evoral::OverlapEnd:
3135 /* overlap starts within but covers end,
3136 so trim the front of the region
3138 original->trim_front (adjusted_start);
3139 modified_region = true;
3142 case Evoral::OverlapStart:
3143 /* overlap covers start but ends within, so
3144 * trim the end of the region.
3146 original->trim_end (adjusted_end);
3147 modified_region = true;
3152 /* fix the position to match any movement of the compound region.
3154 original->set_position (original->position() + move_offset);
3155 modified_region = true;
3158 if (modified_region) {
3159 _session.add_command (new StatefulDiffCommand (original));
3162 /* and add to the list of regions waiting to be
3166 originals.push_back (original);
3167 old_and_new_regions.push_back (TwoRegions (*i, original));
3170 pre_uncombine (originals, target);
3172 in_partition = true;
3175 // (3) remove the compound region
3177 remove_region (target);
3179 // (4) add the constituent regions
3181 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3182 add_region ((*i), (*i)->position());
3183 set_layer((*i), (*i)->layer());
3184 if (!RegionFactory::region_by_id((*i)->id())) {
3185 RegionFactory::map_add(*i);
3189 in_partition = false;
3194 Playlist::fade_range (list<AudioRange>& ranges)
3196 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3197 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3198 (*i)->fade_range ((*r).start, (*r).end);
3204 Playlist::max_source_level () const
3206 RegionReadLock rlock (const_cast<Playlist *> (this));
3209 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3210 lvl = max (lvl, (*i)->max_source_level());
3217 Playlist::set_orig_track_id (const PBD::ID& id)
3219 _orig_track_id = id;
3222 /** Take a list of ranges, coalesce any that can be coalesced, then call
3223 * check_crossfades for each one.
3226 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3228 /* XXX: it's a shame that this coalesce algorithm also exists in
3229 TimeSelection::consolidate().
3232 /* XXX: xfade: this is implemented in Evoral::RangeList */
3235 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3236 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3242 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3243 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3244 i->from = min (i->from, j->from);
3245 i->to = max (i->to, j->to);
3254 Playlist::set_capture_insertion_in_progress (bool yn)
3256 _capture_insertion_underway = yn;