2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <boost/lexical_cast.hpp>
31 #include "pbd/convert.h"
32 #include "pbd/failed_constructor.h"
33 #include "pbd/stateful_diff_command.h"
34 #include "pbd/xml++.h"
35 #include "pbd/stacktrace.h"
37 #include "ardour/debug.h"
38 #include "ardour/playlist.h"
39 #include "ardour/session.h"
40 #include "ardour/region.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/region_sorters.h"
43 #include "ardour/playlist_factory.h"
44 #include "ardour/playlist_source.h"
45 #include "ardour/transient_detector.h"
46 #include "ardour/session_playlists.h"
47 #include "ardour/source_factory.h"
52 using namespace ARDOUR;
56 namespace Properties {
57 PBD::PropertyDescriptor<bool> regions;
61 struct ShowMeTheList {
62 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
64 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
66 boost::shared_ptr<Playlist> playlist;
73 Playlist::make_property_quarks ()
75 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
76 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
77 Properties::regions.property_id));
80 RegionListProperty::RegionListProperty (Playlist& pl)
81 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
87 RegionListProperty::RegionListProperty (RegionListProperty const & p)
88 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
89 , _playlist (p._playlist)
95 RegionListProperty::clone () const
97 return new RegionListProperty (*this);
101 RegionListProperty::create () const
103 return new RegionListProperty (_playlist);
107 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
109 /* All regions (even those which are deleted) have their state saved by other
110 code, so we can just store ID here.
113 node.add_property ("id", region->id().to_s ());
116 boost::shared_ptr<Region>
117 RegionListProperty::get_content_from_xml (XMLNode const & node) const
119 XMLProperty const * prop = node.property ("id");
122 PBD::ID id (prop->value ());
124 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
127 ret = RegionFactory::region_by_id (id);
133 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
134 : SessionObject(sess, nom)
139 first_set_state = false;
144 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
145 : SessionObject(sess, "unnamed playlist")
150 const XMLProperty* prop = node.property("type");
151 assert(!prop || DataType(prop->value()) == _type);
155 _name = "unnamed"; /* reset by set_state */
158 /* set state called by derived class */
161 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
162 : SessionObject(other->_session, namestr)
164 , _type(other->_type)
165 , _orig_track_id (other->_orig_track_id)
170 other->copy_regions (tmp);
174 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
175 add_region_internal( (*x), (*x)->position());
180 _splicing = other->_splicing;
181 _nudging = other->_nudging;
182 _edit_mode = other->_edit_mode;
185 first_set_state = false;
187 in_partition = false;
189 _frozen = other->_frozen;
192 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
193 : SessionObject(other->_session, str)
195 , _type(other->_type)
196 , _orig_track_id (other->_orig_track_id)
198 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
200 framepos_t end = start + cnt - 1;
206 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
208 boost::shared_ptr<Region> region;
209 boost::shared_ptr<Region> new_region;
210 frameoffset_t offset = 0;
211 framepos_t position = 0;
214 Evoral::OverlapType overlap;
218 overlap = region->coverage (start, end);
221 case Evoral::OverlapNone:
224 case Evoral::OverlapInternal:
225 offset = start - region->position();
230 case Evoral::OverlapStart:
232 position = region->position() - start;
233 len = end - region->position();
236 case Evoral::OverlapEnd:
237 offset = start - region->position();
239 len = region->length() - offset;
242 case Evoral::OverlapExternal:
244 position = region->position() - start;
245 len = region->length();
249 RegionFactory::region_name (new_name, region->name(), false);
253 plist.add (Properties::start, region->start() + offset);
254 plist.add (Properties::length, len);
255 plist.add (Properties::name, new_name);
256 plist.add (Properties::layer, region->layer());
257 plist.add (Properties::layering_index, region->layering_index());
259 new_region = RegionFactory::RegionFactory::create (region, plist);
261 add_region_internal (new_region, position);
265 first_set_state = false;
272 InUse (true); /* EMIT SIGNAL */
283 InUse (false); /* EMIT SIGNAL */
288 Playlist::copy_regions (RegionList& newlist) const
290 RegionReadLock rlock (const_cast<Playlist *> (this));
292 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
293 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
298 Playlist::init (bool hide)
300 add_property (regions);
301 _xml_node_name = X_("Playlist");
303 g_atomic_int_set (&block_notifications, 0);
304 g_atomic_int_set (&ignore_state_changes, 0);
305 pending_contents_change = false;
306 pending_layering = false;
307 first_set_state = true;
315 _edit_mode = Config->get_edit_mode();
317 in_partition = false;
322 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
323 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
325 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
328 Playlist::~Playlist ()
330 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
333 RegionReadLock rl (this);
335 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
336 (*i)->set_playlist (boost::shared_ptr<Playlist>());
340 /* GoingAway must be emitted by derived classes */
344 Playlist::_set_sort_id ()
347 Playlists are given names like <track name>.<id>
348 or <track name>.<edit group name>.<id> where id
349 is an integer. We extract the id and sort by that.
352 size_t dot_position = _name.val().find_last_of(".");
354 if (dot_position == string::npos) {
357 string t = _name.val().substr(dot_position + 1);
360 _sort_id = boost::lexical_cast<int>(t);
363 catch (boost::bad_lexical_cast e) {
370 Playlist::set_name (const string& str)
372 /* in a typical situation, a playlist is being used
373 by one diskstream and also is referenced by the
374 Session. if there are more references than that,
375 then don't change the name.
382 bool ret = SessionObject::set_name(str);
389 /***********************************************************************
390 CHANGE NOTIFICATION HANDLING
392 Notifications must be delayed till the region_lock is released. This
393 is necessary because handlers for the signals may need to acquire
394 the lock (e.g. to read from the playlist).
395 ***********************************************************************/
398 Playlist::begin_undo ()
405 Playlist::end_undo ()
414 delay_notifications ();
415 g_atomic_int_inc (&ignore_state_changes);
418 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
420 Playlist::thaw (bool from_undo)
422 g_atomic_int_dec_and_test (&ignore_state_changes);
423 release_notifications (from_undo);
428 Playlist::delay_notifications ()
430 g_atomic_int_inc (&block_notifications);
433 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
435 Playlist::release_notifications (bool from_undo)
437 if (g_atomic_int_dec_and_test (&block_notifications)) {
438 flush_notifications (from_undo);
443 Playlist::notify_contents_changed ()
445 if (holding_state ()) {
446 pending_contents_change = true;
448 pending_contents_change = false;
449 ContentsChanged(); /* EMIT SIGNAL */
454 Playlist::notify_layering_changed ()
456 if (holding_state ()) {
457 pending_layering = true;
459 pending_layering = false;
460 LayeringChanged(); /* EMIT SIGNAL */
465 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
467 if (holding_state ()) {
468 pending_removes.insert (r);
469 pending_contents_change = true;
471 /* this might not be true, but we have to act
472 as though it could be.
474 pending_contents_change = false;
475 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
476 ContentsChanged (); /* EMIT SIGNAL */
481 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
483 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
485 if (holding_state ()) {
487 pending_range_moves.push_back (move);
491 list< Evoral::RangeMove<framepos_t> > m;
493 RangesMoved (m, false);
499 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
501 if (r->position() >= r->last_position()) {
502 /* trimmed shorter */
506 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
508 if (holding_state ()) {
510 pending_region_extensions.push_back (extra);
514 list<Evoral::Range<framepos_t> > r;
522 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
524 if (r->length() < r->last_length()) {
525 /* trimmed shorter */
528 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
530 if (holding_state ()) {
532 pending_region_extensions.push_back (extra);
536 list<Evoral::Range<framepos_t> > r;
544 Playlist::notify_region_added (boost::shared_ptr<Region> r)
546 /* the length change might not be true, but we have to act
547 as though it could be.
550 if (holding_state()) {
551 pending_adds.insert (r);
552 pending_contents_change = true;
555 pending_contents_change = false;
556 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
557 ContentsChanged (); /* EMIT SIGNAL */
561 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
563 Playlist::flush_notifications (bool from_undo)
565 set<boost::shared_ptr<Region> >::iterator s;
566 bool regions_changed = false;
574 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
575 regions_changed = true;
578 /* XXX: it'd be nice if we could use pending_bounds for
579 RegionsExtended and RegionsMoved.
582 /* we have no idea what order the regions ended up in pending
583 bounds (it could be based on selection order, for example).
584 so, to preserve layering in the "most recently moved is higher"
585 model, sort them by existing layer, then timestamp them.
588 // RegionSortByLayer cmp;
589 // pending_bounds.sort (cmp);
591 list<Evoral::Range<framepos_t> > crossfade_ranges;
593 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
594 crossfade_ranges.push_back ((*r)->last_range ());
595 crossfade_ranges.push_back ((*r)->range ());
598 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
599 crossfade_ranges.push_back ((*s)->range ());
600 remove_dependents (*s);
601 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
604 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
605 crossfade_ranges.push_back ((*s)->range ());
606 /* don't emit RegionAdded signal until relayering is done,
607 so that the region is fully setup by the time
608 anyone hears that its been added
612 if (((regions_changed || pending_contents_change) && !in_set_state) || pending_layering) {
616 if (regions_changed || pending_contents_change) {
617 pending_contents_change = false;
618 ContentsChanged (); /* EMIT SIGNAL */
621 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
622 (*s)->clear_changes ();
623 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
626 coalesce_and_check_crossfades (crossfade_ranges);
628 if (!pending_range_moves.empty ()) {
629 /* We don't need to check crossfades for these as pending_bounds has
632 RangesMoved (pending_range_moves, from_undo);
635 if (!pending_region_extensions.empty ()) {
636 RegionsExtended (pending_region_extensions);
645 Playlist::clear_pending ()
647 pending_adds.clear ();
648 pending_removes.clear ();
649 pending_bounds.clear ();
650 pending_range_moves.clear ();
651 pending_region_extensions.clear ();
652 pending_contents_change = false;
655 /*************************************************************
657 *************************************************************/
659 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
661 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
663 RegionWriteLock rlock (this);
664 times = fabs (times);
666 int itimes = (int) floor (times);
668 framepos_t pos = position;
670 if (times == 1 && auto_partition){
671 partition(pos - 1, (pos + region->length()), true);
675 add_region_internal (region, pos);
676 set_layer (region, DBL_MAX);
677 pos += region->length();
682 /* note that itimes can be zero if we being asked to just
683 insert a single fraction of the region.
686 for (int i = 0; i < itimes; ++i) {
687 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
688 add_region_internal (copy, pos);
689 set_layer (copy, DBL_MAX);
690 pos += region->length();
693 framecnt_t length = 0;
695 if (floor (times) != times) {
696 length = (framecnt_t) floor (region->length() * (times - floor (times)));
698 RegionFactory::region_name (name, region->name(), false);
703 plist.add (Properties::start, region->start());
704 plist.add (Properties::length, length);
705 plist.add (Properties::name, name);
706 plist.add (Properties::layer, region->layer());
708 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
709 add_region_internal (sub, pos);
710 set_layer (sub, DBL_MAX);
714 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
718 Playlist::set_region_ownership ()
720 RegionWriteLock rl (this);
721 RegionList::iterator i;
722 boost::weak_ptr<Playlist> pl (shared_from_this());
724 for (i = regions.begin(); i != regions.end(); ++i) {
725 (*i)->set_playlist (pl);
730 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
732 if (region->data_type() != _type) {
736 RegionSortByPosition cmp;
738 if (!first_set_state) {
739 boost::shared_ptr<Playlist> foo (shared_from_this());
740 region->set_playlist (boost::weak_ptr<Playlist>(foo));
743 region->set_position (position);
745 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
746 all_regions.insert (region);
748 possibly_splice_unlocked (position, region->length(), region);
750 if (!holding_state ()) {
751 /* layers get assigned from XML state, and are not reset during undo/redo */
755 /* we need to notify the existence of new region before checking dependents. Ick. */
757 notify_region_added (region);
759 if (!holding_state ()) {
760 check_crossfades (region->range ());
763 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
769 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
771 RegionWriteLock rlock (this);
773 bool old_sp = _splicing;
776 remove_region_internal (old);
777 add_region_internal (newr, pos);
778 set_layer (newr, old->layer ());
782 possibly_splice_unlocked (pos, old->length() - newr->length());
786 Playlist::remove_region (boost::shared_ptr<Region> region)
788 RegionWriteLock rlock (this);
789 remove_region_internal (region);
793 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
795 RegionList::iterator i;
799 region->set_playlist (boost::weak_ptr<Playlist>());
802 /* XXX should probably freeze here .... */
804 for (i = regions.begin(); i != regions.end(); ++i) {
807 framepos_t pos = (*i)->position();
808 framecnt_t distance = (*i)->length();
812 possibly_splice_unlocked (pos, -distance);
814 if (!holding_state ()) {
816 remove_dependents (region);
819 notify_region_removed (region);
828 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
830 if (Config->get_use_overlap_equivalency()) {
831 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
832 if ((*i)->overlap_equivalent (other)) {
833 results.push_back (*i);
837 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
838 if ((*i)->equivalent (other)) {
839 results.push_back (*i);
846 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
848 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
850 if ((*i) && (*i)->region_list_equivalent (other)) {
851 results.push_back (*i);
857 Playlist::partition (framepos_t start, framepos_t end, bool cut)
861 partition_internal (start, end, cut, thawlist);
863 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
864 (*i)->resume_property_changes ();
868 /** Go through each region on the playlist and cut them at start and end, removing the section between
869 * start and end if cutting == true. Regions that lie entirely within start and end are always
874 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
876 RegionList new_regions;
879 RegionWriteLock rlock (this);
881 boost::shared_ptr<Region> region;
882 boost::shared_ptr<Region> current;
884 RegionList::iterator tmp;
885 Evoral::OverlapType overlap;
886 framepos_t pos1, pos2, pos3, pos4;
890 /* need to work from a copy, because otherwise the regions we add during the process
891 get operated on as well.
894 RegionList copy = regions.rlist();
896 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
903 if (current->first_frame() >= start && current->last_frame() < end) {
906 remove_region_internal (current);
912 /* coverage will return OverlapStart if the start coincides
913 with the end point. we do not partition such a region,
914 so catch this special case.
917 if (current->first_frame() >= end) {
921 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
925 pos1 = current->position();
928 pos4 = current->last_frame();
930 if (overlap == Evoral::OverlapInternal) {
931 /* split: we need 3 new regions, the front, middle and end.
932 cut: we need 2 regions, the front and end.
937 ---------------*************************------------
940 ---------------*****++++++++++++++++====------------
942 ---------------*****----------------====------------
947 /* "middle" ++++++ */
949 RegionFactory::region_name (new_name, current->name(), false);
953 plist.add (Properties::start, current->start() + (pos2 - pos1));
954 plist.add (Properties::length, pos3 - pos2);
955 plist.add (Properties::name, new_name);
956 plist.add (Properties::layer, current->layer ());
957 plist.add (Properties::layering_index, current->layering_index ());
958 plist.add (Properties::automatic, true);
959 plist.add (Properties::left_of_split, true);
960 plist.add (Properties::right_of_split, true);
962 region = RegionFactory::create (current, plist);
963 add_region_internal (region, start);
964 new_regions.push_back (region);
969 RegionFactory::region_name (new_name, current->name(), false);
973 plist.add (Properties::start, current->start() + (pos3 - pos1));
974 plist.add (Properties::length, pos4 - pos3);
975 plist.add (Properties::name, new_name);
976 plist.add (Properties::layer, current->layer ());
977 plist.add (Properties::layering_index, current->layering_index ());
978 plist.add (Properties::automatic, true);
979 plist.add (Properties::right_of_split, true);
981 region = RegionFactory::create (current, plist);
983 add_region_internal (region, end);
984 new_regions.push_back (region);
988 current->suspend_property_changes ();
989 thawlist.push_back (current);
990 current->cut_end (pos2 - 1);
992 } else if (overlap == Evoral::OverlapEnd) {
996 ---------------*************************------------
999 ---------------**************+++++++++++------------
1001 ---------------**************-----------------------
1008 RegionFactory::region_name (new_name, current->name(), false);
1012 plist.add (Properties::start, current->start() + (pos2 - pos1));
1013 plist.add (Properties::length, pos4 - pos2);
1014 plist.add (Properties::name, new_name);
1015 plist.add (Properties::layer, current->layer ());
1016 plist.add (Properties::layering_index, current->layering_index ());
1017 plist.add (Properties::automatic, true);
1018 plist.add (Properties::left_of_split, true);
1020 region = RegionFactory::create (current, plist);
1022 add_region_internal (region, start);
1023 new_regions.push_back (region);
1028 current->suspend_property_changes ();
1029 thawlist.push_back (current);
1030 current->cut_end (pos2 - 1);
1032 } else if (overlap == Evoral::OverlapStart) {
1034 /* split: we need 2 regions: the front and the end.
1035 cut: just trim current to skip the cut area
1040 ---------------*************************------------
1044 ---------------****+++++++++++++++++++++------------
1046 -------------------*********************------------
1052 RegionFactory::region_name (new_name, current->name(), false);
1056 plist.add (Properties::start, current->start());
1057 plist.add (Properties::length, pos3 - pos1);
1058 plist.add (Properties::name, new_name);
1059 plist.add (Properties::layer, current->layer ());
1060 plist.add (Properties::layering_index, current->layering_index ());
1061 plist.add (Properties::automatic, true);
1062 plist.add (Properties::right_of_split, true);
1064 region = RegionFactory::create (current, plist);
1066 add_region_internal (region, pos1);
1067 new_regions.push_back (region);
1072 current->suspend_property_changes ();
1073 thawlist.push_back (current);
1074 current->trim_front (pos3);
1075 } else if (overlap == Evoral::OverlapExternal) {
1077 /* split: no split required.
1078 cut: remove the region.
1083 ---------------*************************------------
1087 ---------------*************************------------
1089 ----------------------------------------------------
1094 remove_region_internal (current);
1097 new_regions.push_back (current);
1101 in_partition = false;
1104 check_crossfades (Evoral::Range<framepos_t> (start, end));
1107 boost::shared_ptr<Playlist>
1108 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1110 boost::shared_ptr<Playlist> ret;
1111 boost::shared_ptr<Playlist> pl;
1114 if (ranges.empty()) {
1115 return boost::shared_ptr<Playlist>();
1118 start = ranges.front().start;
1120 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1122 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1124 if (i == ranges.begin()) {
1128 /* paste the next section into the nascent playlist,
1129 offset to reflect the start of the first range we
1133 ret->paste (pl, (*i).start - start, 1.0f);
1140 boost::shared_ptr<Playlist>
1141 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1143 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1144 return cut_copy (pmf, ranges, result_is_hidden);
1147 boost::shared_ptr<Playlist>
1148 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1150 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1151 return cut_copy (pmf, ranges, result_is_hidden);
1154 boost::shared_ptr<Playlist>
1155 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1157 boost::shared_ptr<Playlist> the_copy;
1158 RegionList thawlist;
1161 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1162 string new_name = _name;
1166 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1167 return boost::shared_ptr<Playlist>();
1170 partition_internal (start, start+cnt-1, true, thawlist);
1172 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1173 (*i)->resume_property_changes();
1179 boost::shared_ptr<Playlist>
1180 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1184 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1185 string new_name = _name;
1189 cnt = min (_get_extent().second - start, cnt);
1190 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1194 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1196 times = fabs (times);
1199 RegionReadLock rl2 (other.get());
1201 int itimes = (int) floor (times);
1202 framepos_t pos = position;
1203 framecnt_t const shift = other->_get_extent().second;
1204 layer_t top = top_layer ();
1207 RegionWriteLock rl1 (this);
1209 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1210 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1212 /* put these new regions on top of all existing ones, but preserve
1213 the ordering they had in the original playlist.
1216 add_region_internal (copy_of_region, (*i)->position() + pos);
1217 set_layer (copy_of_region, copy_of_region->layer() + top);
1229 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1231 times = fabs (times);
1233 RegionWriteLock rl (this);
1234 int itimes = (int) floor (times);
1235 framepos_t pos = position + 1;
1238 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1239 add_region_internal (copy, pos);
1240 set_layer (copy, DBL_MAX);
1241 pos += region->length();
1244 if (floor (times) != times) {
1245 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1247 RegionFactory::region_name (name, region->name(), false);
1252 plist.add (Properties::start, region->start());
1253 plist.add (Properties::length, length);
1254 plist.add (Properties::name, name);
1256 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1257 add_region_internal (sub, pos);
1258 set_layer (sub, DBL_MAX);
1264 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1266 RegionWriteLock rlock (this);
1267 RegionList copy (regions.rlist());
1270 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1272 if ((*r)->last_frame() < at) {
1277 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1278 /* intersected region */
1279 if (!move_intersected) {
1284 /* do not move regions glued to music time - that
1285 has to be done separately.
1288 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1289 fixup.push_back (*r);
1293 (*r)->set_position ((*r)->position() + distance);
1296 /* XXX: may not be necessary; Region::post_set should do this, I think */
1297 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1298 (*r)->recompute_position_from_lock_style ();
1303 Playlist::split (framepos_t at)
1305 RegionWriteLock rlock (this);
1306 RegionList copy (regions.rlist());
1308 /* use a copy since this operation can modify the region list
1311 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1312 _split_region (*r, at);
1317 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1319 RegionWriteLock rl (this);
1320 _split_region (region, playlist_position);
1324 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1326 if (!region->covers (playlist_position)) {
1330 if (region->position() == playlist_position ||
1331 region->last_frame() == playlist_position) {
1335 boost::shared_ptr<Region> left;
1336 boost::shared_ptr<Region> right;
1337 frameoffset_t before;
1338 frameoffset_t after;
1342 /* split doesn't change anything about length, so don't try to splice */
1344 bool old_sp = _splicing;
1347 before = playlist_position - region->position();
1348 after = region->length() - before;
1350 RegionFactory::region_name (before_name, region->name(), false);
1355 plist.add (Properties::position, region->position ());
1356 plist.add (Properties::length, before);
1357 plist.add (Properties::name, before_name);
1358 plist.add (Properties::left_of_split, true);
1359 plist.add (Properties::layering_index, region->layering_index ());
1360 plist.add (Properties::layer, region->layer ());
1362 /* note: we must use the version of ::create with an offset here,
1363 since it supplies that offset to the Region constructor, which
1364 is necessary to get audio region gain envelopes right.
1366 left = RegionFactory::create (region, 0, plist);
1369 RegionFactory::region_name (after_name, region->name(), false);
1374 plist.add (Properties::position, region->position() + before);
1375 plist.add (Properties::length, after);
1376 plist.add (Properties::name, after_name);
1377 plist.add (Properties::right_of_split, true);
1378 plist.add (Properties::layering_index, region->layering_index ());
1379 plist.add (Properties::layer, region->layer ());
1381 /* same note as above */
1382 right = RegionFactory::create (region, before, plist);
1385 add_region_internal (left, region->position());
1386 add_region_internal (right, region->position() + before);
1387 remove_region_internal (region);
1393 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1395 if (_splicing || in_set_state) {
1396 /* don't respond to splicing moves or state setting */
1400 if (_edit_mode == Splice) {
1401 splice_locked (at, distance, exclude);
1406 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1408 if (_splicing || in_set_state) {
1409 /* don't respond to splicing moves or state setting */
1413 if (_edit_mode == Splice) {
1414 splice_unlocked (at, distance, exclude);
1419 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1422 RegionWriteLock rl (this);
1423 core_splice (at, distance, exclude);
1428 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1430 core_splice (at, distance, exclude);
1434 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1438 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1440 if (exclude && (*i) == exclude) {
1444 if ((*i)->position() >= at) {
1445 framepos_t new_pos = (*i)->position() + distance;
1448 } else if (new_pos >= max_framepos - (*i)->length()) {
1449 new_pos = max_framepos - (*i)->length();
1452 (*i)->set_position (new_pos);
1458 notify_contents_changed ();
1462 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1464 if (in_set_state || _splicing || _nudging || _shuffling) {
1468 if (what_changed.contains (Properties::position)) {
1470 /* remove it from the list then add it back in
1471 the right place again.
1474 RegionSortByPosition cmp;
1476 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1478 if (i == regions.end()) {
1479 /* the region bounds are being modified but its not currently
1480 in the region list. we will use its bounds correctly when/if
1487 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1490 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1492 frameoffset_t delta = 0;
1494 if (what_changed.contains (Properties::position)) {
1495 delta = region->position() - region->last_position();
1498 if (what_changed.contains (Properties::length)) {
1499 delta += region->length() - region->last_length();
1503 possibly_splice (region->last_position() + region->last_length(), delta, region);
1506 if (holding_state ()) {
1507 pending_bounds.push_back (region);
1509 notify_contents_changed ();
1511 list<Evoral::Range<framepos_t> > xf;
1512 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1513 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1514 coalesce_and_check_crossfades (xf);
1520 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1522 boost::shared_ptr<Region> region (weak_region.lock());
1528 /* this makes a virtual call to the right kind of playlist ... */
1530 region_changed (what_changed, region);
1534 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1536 PropertyChange our_interests;
1537 PropertyChange bounds;
1538 PropertyChange pos_and_length;
1541 if (in_set_state || in_flush) {
1545 our_interests.add (Properties::muted);
1546 our_interests.add (Properties::layer);
1547 our_interests.add (Properties::opaque);
1549 bounds.add (Properties::start);
1550 bounds.add (Properties::position);
1551 bounds.add (Properties::length);
1553 pos_and_length.add (Properties::position);
1554 pos_and_length.add (Properties::length);
1556 if (what_changed.contains (bounds)) {
1557 region_bounds_changed (what_changed, region);
1558 save = !(_splicing || _nudging);
1561 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1562 check_crossfades (region->range ());
1565 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1566 notify_region_moved (region);
1567 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1568 notify_region_end_trimmed (region);
1569 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1570 notify_region_start_trimmed (region);
1573 /* don't notify about layer changes, since we are the only object that can initiate
1574 them, and we notify in ::relayer()
1577 if (what_changed.contains (our_interests)) {
1585 Playlist::drop_regions ()
1587 RegionWriteLock rl (this);
1589 all_regions.clear ();
1593 Playlist::sync_all_regions_with_regions ()
1595 RegionWriteLock rl (this);
1597 all_regions.clear ();
1599 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1600 all_regions.insert (*i);
1605 Playlist::clear (bool with_signals)
1608 RegionWriteLock rl (this);
1610 region_state_changed_connections.drop_connections ();
1612 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1613 pending_removes.insert (*i);
1618 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1619 remove_dependents (*s);
1625 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1626 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1629 pending_removes.clear ();
1630 pending_contents_change = false;
1636 /***********************************************************************
1638 **********************************************************************/
1640 boost::shared_ptr<RegionList>
1641 Playlist::regions_at (framepos_t frame)
1643 RegionReadLock rlock (this);
1644 return find_regions_at (frame);
1648 Playlist::count_regions_at (framepos_t frame) const
1650 RegionReadLock rlock (const_cast<Playlist*>(this));
1653 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1654 if ((*i)->covers (frame)) {
1662 boost::shared_ptr<Region>
1663 Playlist::top_region_at (framepos_t frame)
1666 RegionReadLock rlock (this);
1667 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1668 boost::shared_ptr<Region> region;
1670 if (rlist->size()) {
1671 RegionSortByLayer cmp;
1673 region = rlist->back();
1679 boost::shared_ptr<Region>
1680 Playlist::top_unmuted_region_at (framepos_t frame)
1683 RegionReadLock rlock (this);
1684 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1686 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1688 RegionList::iterator tmp = i;
1691 if ((*i)->muted()) {
1698 boost::shared_ptr<Region> region;
1700 if (rlist->size()) {
1701 RegionSortByLayer cmp;
1703 region = rlist->back();
1709 boost::shared_ptr<RegionList>
1710 Playlist::find_regions_at (framepos_t frame)
1712 /* Caller must hold lock */
1714 boost::shared_ptr<RegionList> rlist (new RegionList);
1716 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1717 if ((*i)->covers (frame)) {
1718 rlist->push_back (*i);
1725 boost::shared_ptr<RegionList>
1726 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1728 RegionReadLock rlock (this);
1729 boost::shared_ptr<RegionList> rlist (new RegionList);
1731 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1732 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1733 rlist->push_back (*i);
1740 boost::shared_ptr<RegionList>
1741 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1743 RegionReadLock rlock (this);
1744 boost::shared_ptr<RegionList> rlist (new RegionList);
1746 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1747 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1748 rlist->push_back (*i);
1755 /** @param start Range start.
1756 * @param end Range end.
1757 * @return regions which have some part within this range.
1759 boost::shared_ptr<RegionList>
1760 Playlist::regions_touched (framepos_t start, framepos_t end)
1762 RegionReadLock rlock (this);
1763 return regions_touched_locked (start, end);
1766 boost::shared_ptr<RegionList>
1767 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1769 boost::shared_ptr<RegionList> rlist (new RegionList);
1771 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1772 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1773 rlist->push_back (*i);
1781 Playlist::find_next_transient (framepos_t from, int dir)
1783 RegionReadLock rlock (this);
1784 AnalysisFeatureList points;
1785 AnalysisFeatureList these_points;
1787 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1789 if ((*i)->last_frame() < from) {
1793 if ((*i)->first_frame() > from) {
1798 (*i)->get_transients (these_points);
1800 /* add first frame, just, err, because */
1802 these_points.push_back ((*i)->first_frame());
1804 points.insert (points.end(), these_points.begin(), these_points.end());
1805 these_points.clear ();
1808 if (points.empty()) {
1812 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1813 bool reached = false;
1816 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1821 if (reached && (*x) > from) {
1826 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1831 if (reached && (*x) < from) {
1840 boost::shared_ptr<Region>
1841 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1843 RegionReadLock rlock (this);
1844 boost::shared_ptr<Region> ret;
1845 framepos_t closest = max_framepos;
1847 bool end_iter = false;
1849 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1853 frameoffset_t distance;
1854 boost::shared_ptr<Region> r = (*i);
1859 pos = r->first_frame ();
1862 pos = r->last_frame ();
1865 pos = r->sync_position ();
1870 case 1: /* forwards */
1873 if ((distance = pos - frame) < closest) {
1882 default: /* backwards */
1885 if ((distance = frame - pos) < closest) {
1901 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1903 RegionReadLock rlock (this);
1905 framepos_t closest = max_framepos;
1906 framepos_t ret = -1;
1910 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1912 boost::shared_ptr<Region> r = (*i);
1913 frameoffset_t distance;
1915 if (r->first_frame() > frame) {
1917 distance = r->first_frame() - frame;
1919 if (distance < closest) {
1920 ret = r->first_frame();
1925 if (r->last_frame () > frame) {
1927 distance = r->last_frame () - frame;
1929 if (distance < closest) {
1930 ret = r->last_frame ();
1938 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1940 boost::shared_ptr<Region> r = (*i);
1941 frameoffset_t distance;
1943 if (r->last_frame() < frame) {
1945 distance = frame - r->last_frame();
1947 if (distance < closest) {
1948 ret = r->last_frame();
1953 if (r->first_frame() < frame) {
1955 distance = frame - r->first_frame();
1957 if (distance < closest) {
1958 ret = r->first_frame();
1969 /***********************************************************************/
1975 Playlist::mark_session_dirty ()
1977 if (!in_set_state && !holding_state ()) {
1978 _session.set_dirty();
1983 Playlist::rdiff (vector<Command*>& cmds) const
1985 RegionReadLock rlock (const_cast<Playlist *> (this));
1986 Stateful::rdiff (cmds);
1990 Playlist::clear_owned_changes ()
1992 RegionReadLock rlock (this);
1993 Stateful::clear_owned_changes ();
1997 Playlist::update (const RegionListProperty::ChangeRecord& change)
1999 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2000 name(), change.added.size(), change.removed.size()));
2003 /* add the added regions */
2004 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2005 add_region_internal ((*i), (*i)->position());
2007 /* remove the removed regions */
2008 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2016 Playlist::set_state (const XMLNode& node, int version)
2020 XMLNodeConstIterator niter;
2021 XMLPropertyList plist;
2022 XMLPropertyConstIterator piter;
2024 boost::shared_ptr<Region> region;
2026 bool seen_region_nodes = false;
2031 if (node.name() != "Playlist") {
2038 plist = node.properties();
2042 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2046 if (prop->name() == X_("name")) {
2047 _name = prop->value();
2049 } else if (prop->name() == X_("orig-diskstream-id")) {
2050 /* XXX legacy session: fix up later */
2051 _orig_track_id = prop->value ();
2052 } else if (prop->name() == X_("orig-track-id")) {
2053 _orig_track_id = prop->value ();
2054 } else if (prop->name() == X_("frozen")) {
2055 _frozen = string_is_affirmative (prop->value());
2056 } else if (prop->name() == X_("combine-ops")) {
2057 _combine_ops = atoi (prop->value());
2063 nlist = node.children();
2065 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2069 if (child->name() == "Region") {
2071 seen_region_nodes = true;
2073 if ((prop = child->property ("id")) == 0) {
2074 error << _("region state node has no ID, ignored") << endmsg;
2078 ID id = prop->value ();
2080 if ((region = region_by_id (id))) {
2082 region->suspend_property_changes ();
2084 if (region->set_state (*child, version)) {
2085 region->resume_property_changes ();
2089 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2090 region->suspend_property_changes ();
2092 error << _("Playlist: cannot create region from XML") << endmsg;
2097 RegionWriteLock rlock (this);
2098 add_region_internal (region, region->position());
2101 region->resume_property_changes ();
2106 if (seen_region_nodes && regions.empty()) {
2110 /* update dependents, which was not done during add_region_internal
2111 due to in_set_state being true
2114 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2115 check_crossfades ((*r)->range ());
2120 notify_contents_changed ();
2123 first_set_state = false;
2129 Playlist::get_state()
2131 return state (true);
2135 Playlist::get_template()
2137 return state (false);
2140 /** @param full_state true to include regions in the returned state, otherwise false.
2143 Playlist::state (bool full_state)
2145 XMLNode *node = new XMLNode (X_("Playlist"));
2148 node->add_property (X_("id"), id().to_s());
2149 node->add_property (X_("name"), _name);
2150 node->add_property (X_("type"), _type.to_string());
2152 _orig_track_id.print (buf, sizeof (buf));
2153 node->add_property (X_("orig-track-id"), buf);
2154 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2157 RegionReadLock rlock (this);
2159 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2160 node->add_property ("combine-ops", buf);
2162 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2163 node->add_child_nocopy ((*i)->get_state());
2168 node->add_child_copy (*_extra_xml);
2175 Playlist::empty() const
2177 RegionReadLock rlock (const_cast<Playlist *>(this));
2178 return regions.empty();
2182 Playlist::n_regions() const
2184 RegionReadLock rlock (const_cast<Playlist *>(this));
2185 return regions.size();
2188 pair<framepos_t, framepos_t>
2189 Playlist::get_extent () const
2191 RegionReadLock rlock (const_cast<Playlist *>(this));
2192 return _get_extent ();
2195 pair<framepos_t, framepos_t>
2196 Playlist::_get_extent () const
2198 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2200 if (regions.empty()) {
2205 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2206 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2207 if (e.first < ext.first) {
2208 ext.first = e.first;
2210 if (e.second > ext.second) {
2211 ext.second = e.second;
2219 Playlist::bump_name (string name, Session &session)
2221 string newname = name;
2224 newname = bump_name_once (newname, '.');
2225 } while (session.playlists->by_name (newname)!=NULL);
2232 Playlist::top_layer() const
2234 RegionReadLock rlock (const_cast<Playlist *> (this));
2237 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2238 top = max (top, (*i)->layer());
2244 Playlist::set_edit_mode (EditMode mode)
2249 struct RelayerSort {
2250 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2251 return a->layering_index() < b->layering_index();
2255 /** Set a new layer for a region. This adjusts the layering indices of all
2256 * regions in the playlist to put the specified region in the appropriate
2257 * place. The actual layering will be fixed up when relayer() happens.
2261 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2263 /* Remove the layer we are setting from our region list, and sort it */
2264 RegionList copy = regions.rlist();
2265 copy.remove (region);
2266 copy.sort (RelayerSort ());
2268 /* Put region back in the right place */
2269 RegionList::iterator i = copy.begin();
2270 while (i != copy.end ()) {
2271 if ((*i)->layer() > new_layer) {
2277 copy.insert (i, region);
2279 setup_layering_indices (copy);
2283 Playlist::setup_layering_indices (RegionList const & regions)
2286 list<Evoral::Range<framepos_t> > xf;
2288 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2289 (*k)->set_layering_index (j++);
2293 /** Take the layering indices of each of our regions, compute the layers
2294 * that they should be on, and write the layers back to the regions.
2297 Playlist::relayer ()
2299 /* never compute layers when setting from XML */
2305 /* Build up a new list of regions on each layer, stored in a set of lists
2306 each of which represent some period of time on some layer. The idea
2307 is to avoid having to search the entire region list to establish whether
2308 each region overlaps another */
2310 /* how many pieces to divide this playlist's time up into */
2311 int const divisions = 512;
2313 /* find the start and end positions of the regions on this playlist */
2314 framepos_t start = INT64_MAX;
2316 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2317 start = min (start, (*i)->position());
2318 end = max (end, (*i)->position() + (*i)->length());
2321 /* hence the size of each time division */
2322 double const division_size = (end - start) / double (divisions);
2324 vector<vector<RegionList> > layers;
2325 layers.push_back (vector<RegionList> (divisions));
2327 /* Sort our regions into layering index order */
2328 RegionList copy = regions.rlist();
2329 copy.sort (RelayerSort ());
2331 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2332 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2333 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2336 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2338 /* find the time divisions that this region covers; if there are no regions on the list,
2339 division_size will equal 0 and in this case we'll just say that
2340 start_division = end_division = 0.
2342 int start_division = 0;
2343 int end_division = 0;
2345 if (division_size > 0) {
2346 start_division = floor ( ((*i)->position() - start) / division_size);
2347 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2348 if (end_division == divisions) {
2353 assert (divisions == 0 || end_division < divisions);
2355 /* find the lowest layer that this region can go on */
2356 size_t j = layers.size();
2358 /* try layer j - 1; it can go on if it overlaps no other region
2359 that is already on that layer
2362 bool overlap = false;
2363 for (int k = start_division; k <= end_division; ++k) {
2364 RegionList::iterator l = layers[j-1][k].begin ();
2365 while (l != layers[j-1][k].end()) {
2366 if ((*l)->overlap_equivalent (*i)) {
2379 /* overlap, so we must use layer j */
2386 if (j == layers.size()) {
2387 /* we need a new layer for this region */
2388 layers.push_back (vector<RegionList> (divisions));
2391 /* put a reference to this region in each of the divisions that it exists in */
2392 for (int k = start_division; k <= end_division; ++k) {
2393 layers[j][k].push_back (*i);
2396 (*i)->set_layer (j);
2399 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2400 relayering because we just removed the only region on the top layer, nothing will
2401 appear to have changed, but the StreamView must still sort itself out. We could
2402 probably keep a note of the top layer last time we relayered, and check that,
2403 but premature optimisation &c...
2405 notify_layering_changed ();
2407 /* This relayer() may have been called as a result of a region removal, in which
2408 case we need to setup layering indices to account for the one that has just
2411 setup_layering_indices (copy);
2415 Playlist::raise_region (boost::shared_ptr<Region> region)
2417 set_layer (region, region->layer() + 1.5);
2419 check_crossfades (region->range ());
2423 Playlist::lower_region (boost::shared_ptr<Region> region)
2425 set_layer (region, region->layer() - 1.5);
2427 check_crossfades (region->range ());
2431 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2433 set_layer (region, DBL_MAX);
2435 check_crossfades (region->range ());
2439 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2441 set_layer (region, -0.5);
2443 check_crossfades (region->range ());
2447 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2449 RegionList::iterator i;
2455 RegionWriteLock rlock (const_cast<Playlist *> (this));
2457 for (i = regions.begin(); i != regions.end(); ++i) {
2459 if ((*i)->position() >= start) {
2465 if ((*i)->last_frame() > max_framepos - distance) {
2466 new_pos = max_framepos - (*i)->length();
2468 new_pos = (*i)->position() + distance;
2473 if ((*i)->position() > distance) {
2474 new_pos = (*i)->position() - distance;
2480 (*i)->set_position (new_pos);
2488 notify_contents_changed ();
2494 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2496 RegionReadLock rlock (const_cast<Playlist*> (this));
2498 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2499 if ((*r)->uses_source (src)) {
2507 boost::shared_ptr<Region>
2508 Playlist::find_region (const ID& id) const
2510 RegionReadLock rlock (const_cast<Playlist*> (this));
2512 /* searches all regions currently in use by the playlist */
2514 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2515 if ((*i)->id() == id) {
2520 return boost::shared_ptr<Region> ();
2524 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2526 RegionReadLock rlock (const_cast<Playlist*> (this));
2529 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2538 boost::shared_ptr<Region>
2539 Playlist::region_by_id (const ID& id) const
2541 /* searches all regions ever added to this playlist */
2543 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2544 if ((*i)->id() == id) {
2548 return boost::shared_ptr<Region> ();
2552 Playlist::dump () const
2554 boost::shared_ptr<Region> r;
2556 cerr << "Playlist \"" << _name << "\" " << endl
2557 << regions.size() << " regions "
2560 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2562 cerr << " " << r->name() << " ["
2563 << r->start() << "+" << r->length()
2573 Playlist::set_frozen (bool yn)
2579 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2583 if (region->locked()) {
2590 RegionWriteLock rlock (const_cast<Playlist*> (this));
2595 RegionList::iterator next;
2597 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2598 if ((*i) == region) {
2602 if (next != regions.end()) {
2604 if ((*next)->locked()) {
2610 if ((*next)->position() != region->last_frame() + 1) {
2611 /* they didn't used to touch, so after shuffle,
2612 just have them swap positions.
2614 new_pos = (*next)->position();
2616 /* they used to touch, so after shuffle,
2617 make sure they still do. put the earlier
2618 region where the later one will end after
2621 new_pos = region->position() + (*next)->length();
2624 (*next)->set_position (region->position());
2625 region->set_position (new_pos);
2627 /* avoid a full sort */
2629 regions.erase (i); // removes the region from the list */
2631 regions.insert (next, region); // adds it back after next
2640 RegionList::iterator prev = regions.end();
2642 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2643 if ((*i) == region) {
2645 if (prev != regions.end()) {
2647 if ((*prev)->locked()) {
2652 if (region->position() != (*prev)->last_frame() + 1) {
2653 /* they didn't used to touch, so after shuffle,
2654 just have them swap positions.
2656 new_pos = region->position();
2658 /* they used to touch, so after shuffle,
2659 make sure they still do. put the earlier
2660 one where the later one will end after
2662 new_pos = (*prev)->position() + region->length();
2665 region->set_position ((*prev)->position());
2666 (*prev)->set_position (new_pos);
2668 /* avoid a full sort */
2670 regions.erase (i); // remove region
2671 regions.insert (prev, region); // insert region before prev
2687 notify_contents_changed();
2693 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2695 RegionReadLock rlock (const_cast<Playlist*> (this));
2697 if (regions.size() > 1) {
2705 Playlist::update_after_tempo_map_change ()
2707 RegionWriteLock rlock (const_cast<Playlist*> (this));
2708 RegionList copy (regions.rlist());
2712 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2713 (*i)->update_after_tempo_map_change ();
2720 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2722 RegionWriteLock rl (this, false);
2723 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2729 Playlist::has_region_at (framepos_t const p) const
2731 RegionReadLock (const_cast<Playlist *> (this));
2733 RegionList::const_iterator i = regions.begin ();
2734 while (i != regions.end() && !(*i)->covers (p)) {
2738 return (i != regions.end());
2741 /** Remove any region that uses a given source */
2743 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2745 RegionWriteLock rl (this);
2747 RegionList::iterator i = regions.begin();
2748 while (i != regions.end()) {
2749 RegionList::iterator j = i;
2752 if ((*i)->uses_source (s)) {
2753 remove_region_internal (*i);
2760 /** Look from a session frame time and find the start time of the next region
2761 * which is on the top layer of this playlist.
2762 * @param t Time to look from.
2763 * @return Position of next top-layered region, or max_framepos if there isn't one.
2766 Playlist::find_next_top_layer_position (framepos_t t) const
2768 RegionReadLock rlock (const_cast<Playlist *> (this));
2770 layer_t const top = top_layer ();
2772 RegionList copy = regions.rlist ();
2773 copy.sort (RegionSortByPosition ());
2775 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2776 if ((*i)->position() >= t && (*i)->layer() == top) {
2777 return (*i)->position();
2781 return max_framepos;
2784 boost::shared_ptr<Region>
2785 Playlist::combine (const RegionList& r)
2788 uint32_t channels = 0;
2790 framepos_t earliest_position = max_framepos;
2791 vector<TwoRegions> old_and_new_regions;
2792 vector<boost::shared_ptr<Region> > originals;
2793 vector<boost::shared_ptr<Region> > copies;
2796 uint32_t max_level = 0;
2798 /* find the maximum depth of all the regions we're combining */
2800 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2801 max_level = max (max_level, (*i)->max_source_level());
2804 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2805 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2807 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2809 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2810 earliest_position = min (earliest_position, (*i)->position());
2813 /* enable this so that we do not try to create xfades etc. as we add
2817 pl->in_partition = true;
2819 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2821 /* copy the region */
2823 boost::shared_ptr<Region> original_region = (*i);
2824 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2826 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2827 originals.push_back (original_region);
2828 copies.push_back (copied_region);
2830 RegionFactory::add_compound_association (original_region, copied_region);
2832 /* make position relative to zero */
2834 pl->add_region (copied_region, original_region->position() - earliest_position);
2836 /* use the maximum number of channels for any region */
2838 channels = max (channels, original_region->n_channels());
2840 /* it will go above the layer of the highest existing region */
2842 layer = max (layer, original_region->layer());
2845 pl->in_partition = false;
2847 pre_combine (copies);
2849 /* now create a new PlaylistSource for each channel in the new playlist */
2852 pair<framepos_t,framepos_t> extent = pl->get_extent();
2854 for (uint32_t chn = 0; chn < channels; ++chn) {
2855 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2859 /* now a new whole-file region using the list of sources */
2861 plist.add (Properties::start, 0);
2862 plist.add (Properties::length, extent.second);
2863 plist.add (Properties::name, parent_name);
2864 plist.add (Properties::whole_file, true);
2866 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2868 /* now the non-whole-file region that we will actually use in the
2873 plist.add (Properties::start, 0);
2874 plist.add (Properties::length, extent.second);
2875 plist.add (Properties::name, child_name);
2876 plist.add (Properties::layer, layer+1);
2878 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2880 /* remove all the selected regions from the current playlist
2885 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2889 /* do type-specific stuff with the originals and the new compound
2893 post_combine (originals, compound_region);
2895 /* add the new region at the right location */
2897 add_region (compound_region, earliest_position);
2903 return compound_region;
2907 Playlist::uncombine (boost::shared_ptr<Region> target)
2909 boost::shared_ptr<PlaylistSource> pls;
2910 boost::shared_ptr<const Playlist> pl;
2911 vector<boost::shared_ptr<Region> > originals;
2912 vector<TwoRegions> old_and_new_regions;
2914 // (1) check that its really a compound region
2916 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
2920 pl = pls->playlist();
2922 framepos_t adjusted_start = 0; // gcc isn't smart enough
2923 framepos_t adjusted_end = 0; // gcc isn't smart enough
2925 /* the leftmost (earliest) edge of the compound region
2926 starts at zero in its source, or larger if it
2927 has been trimmed or content-scrolled.
2929 the rightmost (latest) edge of the compound region
2930 relative to its source is the starting point plus
2931 the length of the region.
2934 // (2) get all the original regions
2936 const RegionList& rl (pl->region_list().rlist());
2937 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2938 frameoffset_t move_offset = 0;
2940 /* there are two possibilities here:
2941 1) the playlist that the playlist source was based on
2942 is us, so just add the originals (which belonged to
2943 us anyway) back in the right place.
2945 2) the playlist that the playlist source was based on
2946 is NOT us, so we need to make copies of each of
2947 the original regions that we find, and add them
2950 bool same_playlist = (pls->original() == id());
2952 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
2954 boost::shared_ptr<Region> current (*i);
2956 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
2958 if (ca == cassocs.end()) {
2962 boost::shared_ptr<Region> original (ca->second);
2963 bool modified_region;
2965 if (i == rl.begin()) {
2966 move_offset = (target->position() - original->position()) - target->start();
2967 adjusted_start = original->position() + target->start();
2968 adjusted_end = adjusted_start + target->length();
2971 if (!same_playlist) {
2972 framepos_t pos = original->position();
2973 /* make a copy, but don't announce it */
2974 original = RegionFactory::create (original, false);
2975 /* the pure copy constructor resets position() to zero,
2978 original->set_position (pos);
2981 /* check to see how the original region (in the
2982 * playlist before compounding occured) overlaps
2983 * with the new state of the compound region.
2986 original->clear_changes ();
2987 modified_region = false;
2989 switch (original->coverage (adjusted_start, adjusted_end)) {
2990 case Evoral::OverlapNone:
2991 /* original region does not cover any part
2992 of the current state of the compound region
2996 case Evoral::OverlapInternal:
2997 /* overlap is just a small piece inside the
2998 * original so trim both ends
3000 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3001 modified_region = true;
3004 case Evoral::OverlapExternal:
3005 /* overlap fully covers original, so leave it
3010 case Evoral::OverlapEnd:
3011 /* overlap starts within but covers end,
3012 so trim the front of the region
3014 original->trim_front (adjusted_start);
3015 modified_region = true;
3018 case Evoral::OverlapStart:
3019 /* overlap covers start but ends within, so
3020 * trim the end of the region.
3022 original->trim_end (adjusted_end);
3023 modified_region = true;
3028 /* fix the position to match any movement of the compound region.
3030 original->set_position (original->position() + move_offset);
3031 modified_region = true;
3034 if (modified_region) {
3035 _session.add_command (new StatefulDiffCommand (original));
3038 /* and add to the list of regions waiting to be
3042 originals.push_back (original);
3043 old_and_new_regions.push_back (TwoRegions (*i, original));
3046 pre_uncombine (originals, target);
3048 in_partition = true;
3051 // (3) remove the compound region
3053 remove_region (target);
3055 // (4) add the constituent regions
3057 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3058 add_region ((*i), (*i)->position());
3061 in_partition = false;
3066 Playlist::max_source_level () const
3068 RegionReadLock rlock (const_cast<Playlist *> (this));
3071 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3072 lvl = max (lvl, (*i)->max_source_level());
3079 Playlist::set_orig_track_id (const PBD::ID& id)
3081 _orig_track_id = id;
3084 /** Take a list of ranges, coalesce any that can be coalesced, then call
3085 * check_crossfades for each one.
3088 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3090 /* XXX: it's a shame that this coalesce algorithm also exists in
3091 TimeSelection::consolidate().
3094 /* XXX: xfade: this is implemented in Evoral::RangeList */
3097 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3098 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3104 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3105 i->from = min (i->from, j->from);
3106 i->to = max (i->to, j->to);
3113 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3114 check_crossfades (*i);