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 RegionWriteLock rl1 (this);
1200 RegionReadLock rl2 (other.get());
1202 int itimes = (int) floor (times);
1203 framepos_t pos = position;
1204 framecnt_t const shift = other->_get_extent().second;
1205 layer_t top = top_layer ();
1208 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1209 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1211 /* put these new regions on top of all existing ones, but preserve
1212 the ordering they had in the original playlist.
1215 add_region_internal (copy_of_region, (*i)->position() + pos);
1216 set_layer (copy_of_region, copy_of_region->layer() + top);
1227 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1229 times = fabs (times);
1231 RegionWriteLock rl (this);
1232 int itimes = (int) floor (times);
1233 framepos_t pos = position + 1;
1236 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1237 add_region_internal (copy, pos);
1238 set_layer (copy, DBL_MAX);
1239 pos += region->length();
1242 if (floor (times) != times) {
1243 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1245 RegionFactory::region_name (name, region->name(), false);
1250 plist.add (Properties::start, region->start());
1251 plist.add (Properties::length, length);
1252 plist.add (Properties::name, name);
1254 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1255 add_region_internal (sub, pos);
1256 set_layer (sub, DBL_MAX);
1262 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1264 RegionWriteLock rlock (this);
1265 RegionList copy (regions.rlist());
1268 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1270 if ((*r)->last_frame() < at) {
1275 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1276 /* intersected region */
1277 if (!move_intersected) {
1282 /* do not move regions glued to music time - that
1283 has to be done separately.
1286 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1287 fixup.push_back (*r);
1291 (*r)->set_position ((*r)->position() + distance);
1294 /* XXX: may not be necessary; Region::post_set should do this, I think */
1295 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1296 (*r)->recompute_position_from_lock_style ();
1301 Playlist::split (framepos_t at)
1303 RegionWriteLock rlock (this);
1304 RegionList copy (regions.rlist());
1306 /* use a copy since this operation can modify the region list
1309 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1310 _split_region (*r, at);
1315 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1317 RegionWriteLock rl (this);
1318 _split_region (region, playlist_position);
1322 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1324 if (!region->covers (playlist_position)) {
1328 if (region->position() == playlist_position ||
1329 region->last_frame() == playlist_position) {
1333 boost::shared_ptr<Region> left;
1334 boost::shared_ptr<Region> right;
1335 frameoffset_t before;
1336 frameoffset_t after;
1340 /* split doesn't change anything about length, so don't try to splice */
1342 bool old_sp = _splicing;
1345 before = playlist_position - region->position();
1346 after = region->length() - before;
1348 RegionFactory::region_name (before_name, region->name(), false);
1353 plist.add (Properties::position, region->position ());
1354 plist.add (Properties::length, before);
1355 plist.add (Properties::name, before_name);
1356 plist.add (Properties::left_of_split, true);
1357 plist.add (Properties::layering_index, region->layering_index ());
1358 plist.add (Properties::layer, region->layer ());
1360 /* note: we must use the version of ::create with an offset here,
1361 since it supplies that offset to the Region constructor, which
1362 is necessary to get audio region gain envelopes right.
1364 left = RegionFactory::create (region, 0, plist);
1367 RegionFactory::region_name (after_name, region->name(), false);
1372 plist.add (Properties::position, region->position() + before);
1373 plist.add (Properties::length, after);
1374 plist.add (Properties::name, after_name);
1375 plist.add (Properties::right_of_split, true);
1376 plist.add (Properties::layering_index, region->layering_index ());
1377 plist.add (Properties::layer, region->layer ());
1379 /* same note as above */
1380 right = RegionFactory::create (region, before, plist);
1383 add_region_internal (left, region->position());
1384 add_region_internal (right, region->position() + before);
1385 remove_region_internal (region);
1391 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1393 if (_splicing || in_set_state) {
1394 /* don't respond to splicing moves or state setting */
1398 if (_edit_mode == Splice) {
1399 splice_locked (at, distance, exclude);
1404 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1406 if (_splicing || in_set_state) {
1407 /* don't respond to splicing moves or state setting */
1411 if (_edit_mode == Splice) {
1412 splice_unlocked (at, distance, exclude);
1417 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1420 RegionWriteLock rl (this);
1421 core_splice (at, distance, exclude);
1426 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1428 core_splice (at, distance, exclude);
1432 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1436 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1438 if (exclude && (*i) == exclude) {
1442 if ((*i)->position() >= at) {
1443 framepos_t new_pos = (*i)->position() + distance;
1446 } else if (new_pos >= max_framepos - (*i)->length()) {
1447 new_pos = max_framepos - (*i)->length();
1450 (*i)->set_position (new_pos);
1456 notify_contents_changed ();
1460 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1462 if (in_set_state || _splicing || _nudging || _shuffling) {
1466 if (what_changed.contains (Properties::position)) {
1468 /* remove it from the list then add it back in
1469 the right place again.
1472 RegionSortByPosition cmp;
1474 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1476 if (i == regions.end()) {
1477 /* the region bounds are being modified but its not currently
1478 in the region list. we will use its bounds correctly when/if
1485 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1488 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1490 frameoffset_t delta = 0;
1492 if (what_changed.contains (Properties::position)) {
1493 delta = region->position() - region->last_position();
1496 if (what_changed.contains (Properties::length)) {
1497 delta += region->length() - region->last_length();
1501 possibly_splice (region->last_position() + region->last_length(), delta, region);
1504 if (holding_state ()) {
1505 pending_bounds.push_back (region);
1507 notify_contents_changed ();
1509 list<Evoral::Range<framepos_t> > xf;
1510 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1511 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1512 coalesce_and_check_crossfades (xf);
1518 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1520 boost::shared_ptr<Region> region (weak_region.lock());
1526 /* this makes a virtual call to the right kind of playlist ... */
1528 region_changed (what_changed, region);
1532 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1534 PropertyChange our_interests;
1535 PropertyChange bounds;
1536 PropertyChange pos_and_length;
1539 if (in_set_state || in_flush) {
1543 our_interests.add (Properties::muted);
1544 our_interests.add (Properties::layer);
1545 our_interests.add (Properties::opaque);
1547 bounds.add (Properties::start);
1548 bounds.add (Properties::position);
1549 bounds.add (Properties::length);
1551 pos_and_length.add (Properties::position);
1552 pos_and_length.add (Properties::length);
1554 if (what_changed.contains (bounds)) {
1555 region_bounds_changed (what_changed, region);
1556 save = !(_splicing || _nudging);
1559 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1560 check_crossfades (region->range ());
1563 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1564 notify_region_moved (region);
1565 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1566 notify_region_end_trimmed (region);
1567 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1568 notify_region_start_trimmed (region);
1571 /* don't notify about layer changes, since we are the only object that can initiate
1572 them, and we notify in ::relayer()
1575 if (what_changed.contains (our_interests)) {
1583 Playlist::drop_regions ()
1585 RegionWriteLock rl (this);
1587 all_regions.clear ();
1591 Playlist::sync_all_regions_with_regions ()
1593 RegionWriteLock rl (this);
1595 all_regions.clear ();
1597 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1598 all_regions.insert (*i);
1603 Playlist::clear (bool with_signals)
1606 RegionWriteLock rl (this);
1608 region_state_changed_connections.drop_connections ();
1610 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1611 pending_removes.insert (*i);
1616 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1617 remove_dependents (*s);
1623 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1624 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1627 pending_removes.clear ();
1628 pending_contents_change = false;
1634 /***********************************************************************
1636 **********************************************************************/
1638 boost::shared_ptr<RegionList>
1639 Playlist::regions_at (framepos_t frame)
1641 RegionReadLock rlock (this);
1642 return find_regions_at (frame);
1646 Playlist::count_regions_at (framepos_t frame) const
1648 RegionReadLock rlock (const_cast<Playlist*>(this));
1651 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1652 if ((*i)->covers (frame)) {
1660 boost::shared_ptr<Region>
1661 Playlist::top_region_at (framepos_t frame)
1664 RegionReadLock rlock (this);
1665 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1666 boost::shared_ptr<Region> region;
1668 if (rlist->size()) {
1669 RegionSortByLayer cmp;
1671 region = rlist->back();
1677 boost::shared_ptr<Region>
1678 Playlist::top_unmuted_region_at (framepos_t frame)
1681 RegionReadLock rlock (this);
1682 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1684 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1686 RegionList::iterator tmp = i;
1689 if ((*i)->muted()) {
1696 boost::shared_ptr<Region> region;
1698 if (rlist->size()) {
1699 RegionSortByLayer cmp;
1701 region = rlist->back();
1707 boost::shared_ptr<RegionList>
1708 Playlist::find_regions_at (framepos_t frame)
1710 /* Caller must hold lock */
1712 boost::shared_ptr<RegionList> rlist (new RegionList);
1714 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1715 if ((*i)->covers (frame)) {
1716 rlist->push_back (*i);
1723 boost::shared_ptr<RegionList>
1724 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1726 RegionReadLock rlock (this);
1727 boost::shared_ptr<RegionList> rlist (new RegionList);
1729 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1730 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1731 rlist->push_back (*i);
1738 boost::shared_ptr<RegionList>
1739 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1741 RegionReadLock rlock (this);
1742 boost::shared_ptr<RegionList> rlist (new RegionList);
1744 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1745 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1746 rlist->push_back (*i);
1753 /** @param start Range start.
1754 * @param end Range end.
1755 * @return regions which have some part within this range.
1757 boost::shared_ptr<RegionList>
1758 Playlist::regions_touched (framepos_t start, framepos_t end)
1760 RegionReadLock rlock (this);
1761 return regions_touched_locked (start, end);
1764 boost::shared_ptr<RegionList>
1765 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1767 boost::shared_ptr<RegionList> rlist (new RegionList);
1769 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1770 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1771 rlist->push_back (*i);
1779 Playlist::find_next_transient (framepos_t from, int dir)
1781 RegionReadLock rlock (this);
1782 AnalysisFeatureList points;
1783 AnalysisFeatureList these_points;
1785 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1787 if ((*i)->last_frame() < from) {
1791 if ((*i)->first_frame() > from) {
1796 (*i)->get_transients (these_points);
1798 /* add first frame, just, err, because */
1800 these_points.push_back ((*i)->first_frame());
1802 points.insert (points.end(), these_points.begin(), these_points.end());
1803 these_points.clear ();
1806 if (points.empty()) {
1810 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1811 bool reached = false;
1814 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1819 if (reached && (*x) > from) {
1824 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1829 if (reached && (*x) < from) {
1838 boost::shared_ptr<Region>
1839 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1841 RegionReadLock rlock (this);
1842 boost::shared_ptr<Region> ret;
1843 framepos_t closest = max_framepos;
1845 bool end_iter = false;
1847 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1851 frameoffset_t distance;
1852 boost::shared_ptr<Region> r = (*i);
1857 pos = r->first_frame ();
1860 pos = r->last_frame ();
1863 pos = r->sync_position ();
1868 case 1: /* forwards */
1871 if ((distance = pos - frame) < closest) {
1880 default: /* backwards */
1883 if ((distance = frame - pos) < closest) {
1899 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1901 RegionReadLock rlock (this);
1903 framepos_t closest = max_framepos;
1904 framepos_t ret = -1;
1908 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1910 boost::shared_ptr<Region> r = (*i);
1911 frameoffset_t distance;
1913 if (r->first_frame() > frame) {
1915 distance = r->first_frame() - frame;
1917 if (distance < closest) {
1918 ret = r->first_frame();
1923 if (r->last_frame () > frame) {
1925 distance = r->last_frame () - frame;
1927 if (distance < closest) {
1928 ret = r->last_frame ();
1936 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1938 boost::shared_ptr<Region> r = (*i);
1939 frameoffset_t distance;
1941 if (r->last_frame() < frame) {
1943 distance = frame - r->last_frame();
1945 if (distance < closest) {
1946 ret = r->last_frame();
1951 if (r->first_frame() < frame) {
1953 distance = frame - r->first_frame();
1955 if (distance < closest) {
1956 ret = r->first_frame();
1967 /***********************************************************************/
1973 Playlist::mark_session_dirty ()
1975 if (!in_set_state && !holding_state ()) {
1976 _session.set_dirty();
1981 Playlist::rdiff (vector<Command*>& cmds) const
1983 RegionReadLock rlock (const_cast<Playlist *> (this));
1984 Stateful::rdiff (cmds);
1988 Playlist::clear_owned_changes ()
1990 RegionReadLock rlock (this);
1991 Stateful::clear_owned_changes ();
1995 Playlist::update (const RegionListProperty::ChangeRecord& change)
1997 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
1998 name(), change.added.size(), change.removed.size()));
2001 /* add the added regions */
2002 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2003 add_region_internal ((*i), (*i)->position());
2005 /* remove the removed regions */
2006 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2014 Playlist::set_state (const XMLNode& node, int version)
2018 XMLNodeConstIterator niter;
2019 XMLPropertyList plist;
2020 XMLPropertyConstIterator piter;
2022 boost::shared_ptr<Region> region;
2024 bool seen_region_nodes = false;
2029 if (node.name() != "Playlist") {
2036 plist = node.properties();
2040 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2044 if (prop->name() == X_("name")) {
2045 _name = prop->value();
2047 } else if (prop->name() == X_("orig-diskstream-id")) {
2048 /* XXX legacy session: fix up later */
2049 _orig_track_id = prop->value ();
2050 } else if (prop->name() == X_("orig-track-id")) {
2051 _orig_track_id = prop->value ();
2052 } else if (prop->name() == X_("frozen")) {
2053 _frozen = string_is_affirmative (prop->value());
2054 } else if (prop->name() == X_("combine-ops")) {
2055 _combine_ops = atoi (prop->value());
2061 nlist = node.children();
2063 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2067 if (child->name() == "Region") {
2069 seen_region_nodes = true;
2071 if ((prop = child->property ("id")) == 0) {
2072 error << _("region state node has no ID, ignored") << endmsg;
2076 ID id = prop->value ();
2078 if ((region = region_by_id (id))) {
2080 region->suspend_property_changes ();
2082 if (region->set_state (*child, version)) {
2083 region->resume_property_changes ();
2087 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2088 region->suspend_property_changes ();
2090 error << _("Playlist: cannot create region from XML") << endmsg;
2095 RegionWriteLock rlock (this);
2096 add_region_internal (region, region->position());
2099 region->resume_property_changes ();
2104 if (seen_region_nodes && regions.empty()) {
2108 /* update dependents, which was not done during add_region_internal
2109 due to in_set_state being true
2112 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2113 check_crossfades ((*r)->range ());
2118 notify_contents_changed ();
2121 first_set_state = false;
2127 Playlist::get_state()
2129 return state (true);
2133 Playlist::get_template()
2135 return state (false);
2138 /** @param full_state true to include regions in the returned state, otherwise false.
2141 Playlist::state (bool full_state)
2143 XMLNode *node = new XMLNode (X_("Playlist"));
2146 node->add_property (X_("id"), id().to_s());
2147 node->add_property (X_("name"), _name);
2148 node->add_property (X_("type"), _type.to_string());
2150 _orig_track_id.print (buf, sizeof (buf));
2151 node->add_property (X_("orig-track-id"), buf);
2152 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2155 RegionReadLock rlock (this);
2157 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2158 node->add_property ("combine-ops", buf);
2160 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2161 node->add_child_nocopy ((*i)->get_state());
2166 node->add_child_copy (*_extra_xml);
2173 Playlist::empty() const
2175 RegionReadLock rlock (const_cast<Playlist *>(this));
2176 return regions.empty();
2180 Playlist::n_regions() const
2182 RegionReadLock rlock (const_cast<Playlist *>(this));
2183 return regions.size();
2186 pair<framepos_t, framepos_t>
2187 Playlist::get_extent () const
2189 RegionReadLock rlock (const_cast<Playlist *>(this));
2190 return _get_extent ();
2193 pair<framepos_t, framepos_t>
2194 Playlist::_get_extent () const
2196 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2198 if (regions.empty()) {
2203 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2204 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2205 if (e.first < ext.first) {
2206 ext.first = e.first;
2208 if (e.second > ext.second) {
2209 ext.second = e.second;
2217 Playlist::bump_name (string name, Session &session)
2219 string newname = name;
2222 newname = bump_name_once (newname, '.');
2223 } while (session.playlists->by_name (newname)!=NULL);
2230 Playlist::top_layer() const
2232 RegionReadLock rlock (const_cast<Playlist *> (this));
2235 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2236 top = max (top, (*i)->layer());
2242 Playlist::set_edit_mode (EditMode mode)
2247 struct RelayerSort {
2248 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2249 return a->layering_index() < b->layering_index();
2253 /** Set a new layer for a region. This adjusts the layering indices of all
2254 * regions in the playlist to put the specified region in the appropriate
2255 * place. The actual layering will be fixed up when relayer() happens.
2259 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2261 /* Remove the layer we are setting from our region list, and sort it */
2262 RegionList copy = regions.rlist();
2263 copy.remove (region);
2264 copy.sort (RelayerSort ());
2266 /* Put region back in the right place */
2267 RegionList::iterator i = copy.begin();
2268 while (i != copy.end ()) {
2269 if ((*i)->layer() > new_layer) {
2275 copy.insert (i, region);
2277 setup_layering_indices (copy);
2281 Playlist::setup_layering_indices (RegionList const & regions)
2284 list<Evoral::Range<framepos_t> > xf;
2286 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2287 (*k)->set_layering_index (j++);
2291 /** Take the layering indices of each of our regions, compute the layers
2292 * that they should be on, and write the layers back to the regions.
2295 Playlist::relayer ()
2297 /* never compute layers when setting from XML */
2303 /* Build up a new list of regions on each layer, stored in a set of lists
2304 each of which represent some period of time on some layer. The idea
2305 is to avoid having to search the entire region list to establish whether
2306 each region overlaps another */
2308 /* how many pieces to divide this playlist's time up into */
2309 int const divisions = 512;
2311 /* find the start and end positions of the regions on this playlist */
2312 framepos_t start = INT64_MAX;
2314 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2315 start = min (start, (*i)->position());
2316 end = max (end, (*i)->position() + (*i)->length());
2319 /* hence the size of each time division */
2320 double const division_size = (end - start) / double (divisions);
2322 vector<vector<RegionList> > layers;
2323 layers.push_back (vector<RegionList> (divisions));
2325 /* Sort our regions into layering index order */
2326 RegionList copy = regions.rlist();
2327 copy.sort (RelayerSort ());
2329 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2330 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2331 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2334 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2336 /* find the time divisions that this region covers; if there are no regions on the list,
2337 division_size will equal 0 and in this case we'll just say that
2338 start_division = end_division = 0.
2340 int start_division = 0;
2341 int end_division = 0;
2343 if (division_size > 0) {
2344 start_division = floor ( ((*i)->position() - start) / division_size);
2345 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2346 if (end_division == divisions) {
2351 assert (divisions == 0 || end_division < divisions);
2353 /* find the lowest layer that this region can go on */
2354 size_t j = layers.size();
2356 /* try layer j - 1; it can go on if it overlaps no other region
2357 that is already on that layer
2360 bool overlap = false;
2361 for (int k = start_division; k <= end_division; ++k) {
2362 RegionList::iterator l = layers[j-1][k].begin ();
2363 while (l != layers[j-1][k].end()) {
2364 if ((*l)->overlap_equivalent (*i)) {
2377 /* overlap, so we must use layer j */
2384 if (j == layers.size()) {
2385 /* we need a new layer for this region */
2386 layers.push_back (vector<RegionList> (divisions));
2389 /* put a reference to this region in each of the divisions that it exists in */
2390 for (int k = start_division; k <= end_division; ++k) {
2391 layers[j][k].push_back (*i);
2394 (*i)->set_layer (j);
2397 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2398 relayering because we just removed the only region on the top layer, nothing will
2399 appear to have changed, but the StreamView must still sort itself out. We could
2400 probably keep a note of the top layer last time we relayered, and check that,
2401 but premature optimisation &c...
2403 notify_layering_changed ();
2405 /* This relayer() may have been called as a result of a region removal, in which
2406 case we need to setup layering indices to account for the one that has just
2409 setup_layering_indices (copy);
2413 Playlist::raise_region (boost::shared_ptr<Region> region)
2415 set_layer (region, region->layer() + 1.5);
2417 check_crossfades (region->range ());
2421 Playlist::lower_region (boost::shared_ptr<Region> region)
2423 set_layer (region, region->layer() - 1.5);
2425 check_crossfades (region->range ());
2429 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2431 set_layer (region, DBL_MAX);
2433 check_crossfades (region->range ());
2437 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2439 set_layer (region, -0.5);
2441 check_crossfades (region->range ());
2445 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2447 RegionList::iterator i;
2453 RegionWriteLock rlock (const_cast<Playlist *> (this));
2455 for (i = regions.begin(); i != regions.end(); ++i) {
2457 if ((*i)->position() >= start) {
2463 if ((*i)->last_frame() > max_framepos - distance) {
2464 new_pos = max_framepos - (*i)->length();
2466 new_pos = (*i)->position() + distance;
2471 if ((*i)->position() > distance) {
2472 new_pos = (*i)->position() - distance;
2478 (*i)->set_position (new_pos);
2486 notify_contents_changed ();
2492 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2494 RegionReadLock rlock (const_cast<Playlist*> (this));
2496 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2497 if ((*r)->uses_source (src)) {
2505 boost::shared_ptr<Region>
2506 Playlist::find_region (const ID& id) const
2508 RegionReadLock rlock (const_cast<Playlist*> (this));
2510 /* searches all regions currently in use by the playlist */
2512 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2513 if ((*i)->id() == id) {
2518 return boost::shared_ptr<Region> ();
2522 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2524 RegionReadLock rlock (const_cast<Playlist*> (this));
2527 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2536 boost::shared_ptr<Region>
2537 Playlist::region_by_id (const ID& id) const
2539 /* searches all regions ever added to this playlist */
2541 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2542 if ((*i)->id() == id) {
2546 return boost::shared_ptr<Region> ();
2550 Playlist::dump () const
2552 boost::shared_ptr<Region> r;
2554 cerr << "Playlist \"" << _name << "\" " << endl
2555 << regions.size() << " regions "
2558 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2560 cerr << " " << r->name() << " ["
2561 << r->start() << "+" << r->length()
2571 Playlist::set_frozen (bool yn)
2577 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2581 if (region->locked()) {
2588 RegionWriteLock rlock (const_cast<Playlist*> (this));
2593 RegionList::iterator next;
2595 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2596 if ((*i) == region) {
2600 if (next != regions.end()) {
2602 if ((*next)->locked()) {
2608 if ((*next)->position() != region->last_frame() + 1) {
2609 /* they didn't used to touch, so after shuffle,
2610 just have them swap positions.
2612 new_pos = (*next)->position();
2614 /* they used to touch, so after shuffle,
2615 make sure they still do. put the earlier
2616 region where the later one will end after
2619 new_pos = region->position() + (*next)->length();
2622 (*next)->set_position (region->position());
2623 region->set_position (new_pos);
2625 /* avoid a full sort */
2627 regions.erase (i); // removes the region from the list */
2629 regions.insert (next, region); // adds it back after next
2638 RegionList::iterator prev = regions.end();
2640 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2641 if ((*i) == region) {
2643 if (prev != regions.end()) {
2645 if ((*prev)->locked()) {
2650 if (region->position() != (*prev)->last_frame() + 1) {
2651 /* they didn't used to touch, so after shuffle,
2652 just have them swap positions.
2654 new_pos = region->position();
2656 /* they used to touch, so after shuffle,
2657 make sure they still do. put the earlier
2658 one where the later one will end after
2660 new_pos = (*prev)->position() + region->length();
2663 region->set_position ((*prev)->position());
2664 (*prev)->set_position (new_pos);
2666 /* avoid a full sort */
2668 regions.erase (i); // remove region
2669 regions.insert (prev, region); // insert region before prev
2685 notify_contents_changed();
2691 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2693 RegionReadLock rlock (const_cast<Playlist*> (this));
2695 if (regions.size() > 1) {
2703 Playlist::update_after_tempo_map_change ()
2705 RegionWriteLock rlock (const_cast<Playlist*> (this));
2706 RegionList copy (regions.rlist());
2710 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2711 (*i)->update_after_tempo_map_change ();
2718 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2720 RegionWriteLock rl (this, false);
2721 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2727 Playlist::has_region_at (framepos_t const p) const
2729 RegionReadLock (const_cast<Playlist *> (this));
2731 RegionList::const_iterator i = regions.begin ();
2732 while (i != regions.end() && !(*i)->covers (p)) {
2736 return (i != regions.end());
2739 /** Remove any region that uses a given source */
2741 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2743 RegionWriteLock rl (this);
2745 RegionList::iterator i = regions.begin();
2746 while (i != regions.end()) {
2747 RegionList::iterator j = i;
2750 if ((*i)->uses_source (s)) {
2751 remove_region_internal (*i);
2758 /** Look from a session frame time and find the start time of the next region
2759 * which is on the top layer of this playlist.
2760 * @param t Time to look from.
2761 * @return Position of next top-layered region, or max_framepos if there isn't one.
2764 Playlist::find_next_top_layer_position (framepos_t t) const
2766 RegionReadLock rlock (const_cast<Playlist *> (this));
2768 layer_t const top = top_layer ();
2770 RegionList copy = regions.rlist ();
2771 copy.sort (RegionSortByPosition ());
2773 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2774 if ((*i)->position() >= t && (*i)->layer() == top) {
2775 return (*i)->position();
2779 return max_framepos;
2782 boost::shared_ptr<Region>
2783 Playlist::combine (const RegionList& r)
2786 uint32_t channels = 0;
2788 framepos_t earliest_position = max_framepos;
2789 vector<TwoRegions> old_and_new_regions;
2790 vector<boost::shared_ptr<Region> > originals;
2791 vector<boost::shared_ptr<Region> > copies;
2794 uint32_t max_level = 0;
2796 /* find the maximum depth of all the regions we're combining */
2798 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2799 max_level = max (max_level, (*i)->max_source_level());
2802 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2803 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2805 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2807 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2808 earliest_position = min (earliest_position, (*i)->position());
2811 /* enable this so that we do not try to create xfades etc. as we add
2815 pl->in_partition = true;
2817 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2819 /* copy the region */
2821 boost::shared_ptr<Region> original_region = (*i);
2822 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2824 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2825 originals.push_back (original_region);
2826 copies.push_back (copied_region);
2828 RegionFactory::add_compound_association (original_region, copied_region);
2830 /* make position relative to zero */
2832 pl->add_region (copied_region, original_region->position() - earliest_position);
2834 /* use the maximum number of channels for any region */
2836 channels = max (channels, original_region->n_channels());
2838 /* it will go above the layer of the highest existing region */
2840 layer = max (layer, original_region->layer());
2843 pl->in_partition = false;
2845 pre_combine (copies);
2847 /* now create a new PlaylistSource for each channel in the new playlist */
2850 pair<framepos_t,framepos_t> extent = pl->get_extent();
2852 for (uint32_t chn = 0; chn < channels; ++chn) {
2853 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2857 /* now a new whole-file region using the list of sources */
2859 plist.add (Properties::start, 0);
2860 plist.add (Properties::length, extent.second);
2861 plist.add (Properties::name, parent_name);
2862 plist.add (Properties::whole_file, true);
2864 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2866 /* now the non-whole-file region that we will actually use in the
2871 plist.add (Properties::start, 0);
2872 plist.add (Properties::length, extent.second);
2873 plist.add (Properties::name, child_name);
2874 plist.add (Properties::layer, layer+1);
2876 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2878 /* remove all the selected regions from the current playlist
2883 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2887 /* do type-specific stuff with the originals and the new compound
2891 post_combine (originals, compound_region);
2893 /* add the new region at the right location */
2895 add_region (compound_region, earliest_position);
2901 return compound_region;
2905 Playlist::uncombine (boost::shared_ptr<Region> target)
2907 boost::shared_ptr<PlaylistSource> pls;
2908 boost::shared_ptr<const Playlist> pl;
2909 vector<boost::shared_ptr<Region> > originals;
2910 vector<TwoRegions> old_and_new_regions;
2912 // (1) check that its really a compound region
2914 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
2918 pl = pls->playlist();
2920 framepos_t adjusted_start = 0; // gcc isn't smart enough
2921 framepos_t adjusted_end = 0; // gcc isn't smart enough
2923 /* the leftmost (earliest) edge of the compound region
2924 starts at zero in its source, or larger if it
2925 has been trimmed or content-scrolled.
2927 the rightmost (latest) edge of the compound region
2928 relative to its source is the starting point plus
2929 the length of the region.
2932 // (2) get all the original regions
2934 const RegionList& rl (pl->region_list().rlist());
2935 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2936 frameoffset_t move_offset = 0;
2938 /* there are two possibilities here:
2939 1) the playlist that the playlist source was based on
2940 is us, so just add the originals (which belonged to
2941 us anyway) back in the right place.
2943 2) the playlist that the playlist source was based on
2944 is NOT us, so we need to make copies of each of
2945 the original regions that we find, and add them
2948 bool same_playlist = (pls->original() == id());
2950 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
2952 boost::shared_ptr<Region> current (*i);
2954 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
2956 if (ca == cassocs.end()) {
2960 boost::shared_ptr<Region> original (ca->second);
2961 bool modified_region;
2963 if (i == rl.begin()) {
2964 move_offset = (target->position() - original->position()) - target->start();
2965 adjusted_start = original->position() + target->start();
2966 adjusted_end = adjusted_start + target->length();
2969 if (!same_playlist) {
2970 framepos_t pos = original->position();
2971 /* make a copy, but don't announce it */
2972 original = RegionFactory::create (original, false);
2973 /* the pure copy constructor resets position() to zero,
2976 original->set_position (pos);
2979 /* check to see how the original region (in the
2980 * playlist before compounding occured) overlaps
2981 * with the new state of the compound region.
2984 original->clear_changes ();
2985 modified_region = false;
2987 switch (original->coverage (adjusted_start, adjusted_end)) {
2988 case Evoral::OverlapNone:
2989 /* original region does not cover any part
2990 of the current state of the compound region
2994 case Evoral::OverlapInternal:
2995 /* overlap is just a small piece inside the
2996 * original so trim both ends
2998 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
2999 modified_region = true;
3002 case Evoral::OverlapExternal:
3003 /* overlap fully covers original, so leave it
3008 case Evoral::OverlapEnd:
3009 /* overlap starts within but covers end,
3010 so trim the front of the region
3012 original->trim_front (adjusted_start);
3013 modified_region = true;
3016 case Evoral::OverlapStart:
3017 /* overlap covers start but ends within, so
3018 * trim the end of the region.
3020 original->trim_end (adjusted_end);
3021 modified_region = true;
3026 /* fix the position to match any movement of the compound region.
3028 original->set_position (original->position() + move_offset);
3029 modified_region = true;
3032 if (modified_region) {
3033 _session.add_command (new StatefulDiffCommand (original));
3036 /* and add to the list of regions waiting to be
3040 originals.push_back (original);
3041 old_and_new_regions.push_back (TwoRegions (*i, original));
3044 pre_uncombine (originals, target);
3046 in_partition = true;
3049 // (3) remove the compound region
3051 remove_region (target);
3053 // (4) add the constituent regions
3055 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3056 add_region ((*i), (*i)->position());
3059 in_partition = false;
3064 Playlist::max_source_level () const
3066 RegionReadLock rlock (const_cast<Playlist *> (this));
3069 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3070 lvl = max (lvl, (*i)->max_source_level());
3077 Playlist::set_orig_track_id (const PBD::ID& id)
3079 _orig_track_id = id;
3082 /** Take a list of ranges, coalesce any that can be coalesced, then call
3083 * check_crossfades for each one.
3086 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3088 /* XXX: it's a shame that this coalesce algorithm also exists in
3089 TimeSelection::consolidate().
3092 /* XXX: xfade: this is implemented in Evoral::RangeList */
3095 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3096 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3102 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3103 i->from = min (i->from, j->from);
3104 i->to = max (i->to, j->to);
3111 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3112 check_crossfades (*i);