2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "pbd/types_convert.h"
26 #include "pbd/stateful_diff_command.h"
27 #include "pbd/strsplit.h"
28 #include "pbd/xml++.h"
30 #include "ardour/debug.h"
31 #include "ardour/midi_region.h"
32 #include "ardour/playlist.h"
33 #include "ardour/playlist_factory.h"
34 #include "ardour/playlist_source.h"
35 #include "ardour/region.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/region_sorters.h"
38 #include "ardour/session.h"
39 #include "ardour/session_playlists.h"
40 #include "ardour/source_factory.h"
41 #include "ardour/tempo.h"
42 #include "ardour/transient_detector.h"
43 #include "ardour/types_convert.h"
48 using namespace ARDOUR;
52 namespace Properties {
53 PBD::PropertyDescriptor<bool> regions;
57 struct ShowMeTheList {
58 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
60 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
62 boost::shared_ptr<Playlist> playlist;
69 Playlist::make_property_quarks ()
71 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
72 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
73 Properties::regions.property_id));
76 RegionListProperty::RegionListProperty (Playlist& pl)
77 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
83 RegionListProperty::RegionListProperty (RegionListProperty const & p)
84 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
85 , _playlist (p._playlist)
91 RegionListProperty::clone () const
93 return new RegionListProperty (*this);
97 RegionListProperty::create () const
99 return new RegionListProperty (_playlist);
103 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
105 /* All regions (even those which are deleted) have their state saved by other
106 code, so we can just store ID here.
109 node.set_property ("id", region->id());
112 boost::shared_ptr<Region>
113 RegionListProperty::get_content_from_xml (XMLNode const & node) const
116 if (!node.get_property ("id", id)) {
120 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
123 ret = RegionFactory::region_by_id (id);
129 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
130 : SessionObject(sess, nom)
135 first_set_state = false;
140 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
141 : SessionObject(sess, "unnamed playlist")
146 XMLProperty const * prop = node.property("type");
147 assert(!prop || DataType(prop->value()) == _type);
151 _name = "unnamed"; /* reset by set_state */
154 /* set state called by derived class */
157 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
158 : SessionObject(other->_session, namestr)
160 , _type(other->_type)
161 , _orig_track_id (other->_orig_track_id)
162 , _shared_with_ids (other->_shared_with_ids)
167 other->copy_regions (tmp);
171 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
172 add_region_internal( (*x), (*x)->position());
177 _splicing = other->_splicing;
178 _rippling = other->_rippling;
179 _nudging = other->_nudging;
180 _edit_mode = other->_edit_mode;
183 first_set_state = false;
185 in_partition = false;
187 _frozen = other->_frozen;
190 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
191 : SessionObject(other->_session, str)
193 , _type(other->_type)
194 , _orig_track_id (other->_orig_track_id)
195 , _shared_with_ids (other->_shared_with_ids)
197 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
199 framepos_t end = start + cnt - 1;
205 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
207 boost::shared_ptr<Region> region;
208 boost::shared_ptr<Region> new_region;
209 frameoffset_t offset = 0;
210 framepos_t position = 0;
213 Evoral::OverlapType overlap;
217 overlap = region->coverage (start, end);
220 case Evoral::OverlapNone:
223 case Evoral::OverlapInternal:
224 offset = start - region->position();
229 case Evoral::OverlapStart:
231 position = region->position() - start;
232 len = end - region->position();
235 case Evoral::OverlapEnd:
236 offset = start - region->position();
238 len = region->length() - offset;
241 case Evoral::OverlapExternal:
243 position = region->position() - start;
244 len = region->length();
248 RegionFactory::region_name (new_name, region->name(), false);
252 plist.add (Properties::start, region->start() + offset);
253 plist.add (Properties::length, len);
254 plist.add (Properties::name, new_name);
255 plist.add (Properties::layer, region->layer());
256 plist.add (Properties::layering_index, region->layering_index());
258 new_region = RegionFactory::create (region, plist);
260 add_region_internal (new_region, position);
263 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
264 //at the end of construction, any length of cnt beyond the extents of the regions is end_space
265 _end_space = cnt - (get_extent().second - get_extent().first);
268 first_set_state = false;
275 InUse (true); /* EMIT SIGNAL */
286 InUse (false); /* EMIT SIGNAL */
291 Playlist::copy_regions (RegionList& newlist) const
293 RegionReadLock rlock (const_cast<Playlist *> (this));
295 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
296 newlist.push_back (RegionFactory::create (*i, true, true));
301 Playlist::init (bool hide)
303 add_property (regions);
304 _xml_node_name = X_("Playlist");
306 g_atomic_int_set (&block_notifications, 0);
307 g_atomic_int_set (&ignore_state_changes, 0);
308 pending_contents_change = false;
309 pending_layering = false;
310 first_set_state = true;
319 _edit_mode = Config->get_edit_mode();
321 in_partition = false;
324 _capture_insertion_underway = false;
328 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
329 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
331 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
334 Playlist::~Playlist ()
336 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
339 RegionReadLock rl (this);
341 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
342 (*i)->set_playlist (boost::shared_ptr<Playlist>());
346 /* GoingAway must be emitted by derived classes */
350 Playlist::_set_sort_id ()
353 Playlists are given names like <track name>.<id>
354 or <track name>.<edit group name>.<id> where id
355 is an integer. We extract the id and sort by that.
358 size_t dot_position = _name.val().find_last_of(".");
360 if (dot_position == string::npos) {
363 string t = _name.val().substr(dot_position + 1);
365 if (!string_to_uint32 (t, _sort_id)) {
372 Playlist::set_name (const string& str)
374 /* in a typical situation, a playlist is being used
375 by one diskstream and also is referenced by the
376 Session. if there are more references than that,
377 then don't change the name.
384 bool ret = SessionObject::set_name(str);
391 /***********************************************************************
392 CHANGE NOTIFICATION HANDLING
394 Notifications must be delayed till the region_lock is released. This
395 is necessary because handlers for the signals may need to acquire
396 the lock (e.g. to read from the playlist).
397 ***********************************************************************/
400 Playlist::begin_undo ()
407 Playlist::end_undo ()
416 delay_notifications ();
417 g_atomic_int_inc (&ignore_state_changes);
420 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
422 Playlist::thaw (bool from_undo)
424 g_atomic_int_dec_and_test (&ignore_state_changes);
425 release_notifications (from_undo);
430 Playlist::delay_notifications ()
432 g_atomic_int_inc (&block_notifications);
435 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
437 Playlist::release_notifications (bool from_undo)
439 if (g_atomic_int_dec_and_test (&block_notifications)) {
440 flush_notifications (from_undo);
445 Playlist::notify_contents_changed ()
447 if (holding_state ()) {
448 pending_contents_change = true;
450 pending_contents_change = false;
451 ContentsChanged(); /* EMIT SIGNAL */
456 Playlist::notify_layering_changed ()
458 if (holding_state ()) {
459 pending_layering = true;
461 pending_layering = false;
462 LayeringChanged(); /* EMIT SIGNAL */
467 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
469 if (holding_state ()) {
470 pending_removes.insert (r);
471 pending_contents_change = true;
473 /* this might not be true, but we have to act
474 as though it could be.
476 pending_contents_change = false;
477 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
478 ContentsChanged (); /* EMIT SIGNAL */
483 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
485 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
487 if (holding_state ()) {
489 pending_range_moves.push_back (move);
493 list< Evoral::RangeMove<framepos_t> > m;
495 RangesMoved (m, false);
501 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
503 if (r->position() >= r->last_position()) {
504 /* trimmed shorter */
508 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
510 if (holding_state ()) {
512 pending_region_extensions.push_back (extra);
516 list<Evoral::Range<framepos_t> > r;
524 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
526 if (r->length() < r->last_length()) {
527 /* trimmed shorter */
530 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
532 if (holding_state ()) {
534 pending_region_extensions.push_back (extra);
538 list<Evoral::Range<framepos_t> > r;
546 Playlist::notify_region_added (boost::shared_ptr<Region> r)
548 /* the length change might not be true, but we have to act
549 as though it could be.
552 if (holding_state()) {
553 pending_adds.insert (r);
554 pending_contents_change = true;
557 pending_contents_change = false;
558 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
559 ContentsChanged (); /* EMIT SIGNAL */
564 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
566 Playlist::flush_notifications (bool from_undo)
568 set<boost::shared_ptr<Region> >::iterator s;
569 bool regions_changed = false;
577 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
578 regions_changed = true;
581 /* XXX: it'd be nice if we could use pending_bounds for
582 RegionsExtended and RegionsMoved.
585 /* we have no idea what order the regions ended up in pending
586 bounds (it could be based on selection order, for example).
587 so, to preserve layering in the "most recently moved is higher"
588 model, sort them by existing layer, then timestamp them.
591 // RegionSortByLayer cmp;
592 // pending_bounds.sort (cmp);
594 list<Evoral::Range<framepos_t> > crossfade_ranges;
596 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
597 crossfade_ranges.push_back ((*r)->last_range ());
598 crossfade_ranges.push_back ((*r)->range ());
601 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
602 crossfade_ranges.push_back ((*s)->range ());
603 remove_dependents (*s);
604 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
607 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
608 crossfade_ranges.push_back ((*s)->range ());
609 /* don't emit RegionAdded signal until relayering is done,
610 so that the region is fully setup by the time
611 anyone hears that its been added
615 /* notify about contents/region changes first so that layering changes
616 * in a UI will take place on the new contents.
619 if (regions_changed || pending_contents_change) {
620 pending_layering = true;
621 ContentsChanged (); /* EMIT SIGNAL */
624 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
625 (*s)->clear_changes ();
626 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
629 if ((regions_changed && !in_set_state) || pending_layering) {
633 coalesce_and_check_crossfades (crossfade_ranges);
635 if (!pending_range_moves.empty ()) {
636 /* We don't need to check crossfades for these as pending_bounds has
639 RangesMoved (pending_range_moves, from_undo);
642 if (!pending_region_extensions.empty ()) {
643 RegionsExtended (pending_region_extensions);
652 Playlist::clear_pending ()
654 pending_adds.clear ();
655 pending_removes.clear ();
656 pending_bounds.clear ();
657 pending_range_moves.clear ();
658 pending_region_extensions.clear ();
659 pending_contents_change = false;
660 pending_layering = false;
663 /*************************************************************
665 *************************************************************/
667 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
669 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition, int32_t sub_num, double quarter_note, bool for_music)
671 RegionWriteLock rlock (this);
672 times = fabs (times);
674 int itimes = (int) floor (times);
676 framepos_t pos = position;
678 if (times == 1 && auto_partition){
680 partition_internal (pos - 1, (pos + region->length()), true, thawlist);
681 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
682 (*i)->resume_property_changes ();
683 _session.add_command (new StatefulDiffCommand (*i));
688 add_region_internal (region, pos, sub_num, quarter_note, for_music);
689 set_layer (region, DBL_MAX);
690 pos += region->length();
694 /* note that itimes can be zero if we being asked to just
695 insert a single fraction of the region.
698 for (int i = 0; i < itimes; ++i) {
699 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
700 add_region_internal (copy, pos, sub_num);
701 set_layer (copy, DBL_MAX);
702 pos += region->length();
705 framecnt_t length = 0;
707 if (floor (times) != times) {
708 length = (framecnt_t) floor (region->length() * (times - floor (times)));
710 RegionFactory::region_name (name, region->name(), false);
715 plist.add (Properties::start, region->start());
716 plist.add (Properties::length, length);
717 plist.add (Properties::name, name);
718 plist.add (Properties::layer, region->layer());
720 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
721 add_region_internal (sub, pos, sub_num);
722 set_layer (sub, DBL_MAX);
726 possibly_splice_unlocked (position, (pos + length) - position, region);
730 Playlist::set_region_ownership ()
732 RegionWriteLock rl (this);
733 RegionList::iterator i;
734 boost::weak_ptr<Playlist> pl (shared_from_this());
736 for (i = regions.begin(); i != regions.end(); ++i) {
737 (*i)->set_playlist (pl);
742 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, int32_t sub_num, double quarter_note, bool for_music)
744 if (region->data_type() != _type) {
748 RegionSortByPosition cmp;
750 if (!first_set_state) {
751 boost::shared_ptr<Playlist> foo (shared_from_this());
752 region->set_playlist (boost::weak_ptr<Playlist>(foo));
755 region->set_position_music (quarter_note);
757 region->set_position (position, sub_num);
760 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
761 all_regions.insert (region);
763 possibly_splice_unlocked (position, region->length(), region);
765 if (!holding_state ()) {
766 /* layers get assigned from XML state, and are not reset during undo/redo */
770 /* we need to notify the existence of new region before checking dependents. Ick. */
772 notify_region_added (region);
774 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
775 region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
781 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
783 RegionWriteLock rlock (this);
785 bool old_sp = _splicing;
788 remove_region_internal (old);
789 add_region_internal (newr, pos);
790 set_layer (newr, old->layer ());
794 possibly_splice_unlocked (pos, old->length() - newr->length());
798 Playlist::remove_region (boost::shared_ptr<Region> region)
800 RegionWriteLock rlock (this);
801 remove_region_internal (region);
805 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
807 RegionList::iterator i;
811 region->set_playlist (boost::weak_ptr<Playlist>());
814 /* XXX should probably freeze here .... */
816 for (i = regions.begin(); i != regions.end(); ++i) {
819 framepos_t pos = (*i)->position();
820 framecnt_t distance = (*i)->length();
824 possibly_splice_unlocked (pos, -distance);
826 if (!holding_state ()) {
828 remove_dependents (region);
831 notify_region_removed (region);
840 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
842 if (Config->get_use_overlap_equivalency()) {
843 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
844 if ((*i)->overlap_equivalent (other)) {
845 results.push_back (*i);
849 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
850 if ((*i)->equivalent (other)) {
851 results.push_back (*i);
858 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
860 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
862 if ((*i) && (*i)->region_list_equivalent (other)) {
863 results.push_back (*i);
869 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
871 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
873 if ((*i) && (*i)->any_source_equivalent (other)) {
874 results.push_back (*i);
880 Playlist::partition (framepos_t start, framepos_t end, bool cut)
884 RegionWriteLock lock(this);
885 partition_internal (start, end, cut, thawlist);
888 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
889 (*i)->resume_property_changes ();
893 /* If a MIDI region is locked to musical-time, Properties::start is ignored
894 * and _start is overwritten using Properties::start_beats in
895 * add_region_internal() -> Region::set_position() -> MidiRegion::set_position_internal()
897 static void maybe_add_start_beats (TempoMap const& tm, PropertyList& plist, boost::shared_ptr<Region> r, framepos_t start, framepos_t end)
899 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(r);
903 double delta_beats = tm.quarter_notes_between_frames (start, end);
904 plist.add (Properties::start_beats, mr->start_beats () + delta_beats);
907 /** Go through each region on the playlist and cut them at start and end, removing the section between
908 * start and end if cutting == true. Regions that lie entirely within start and end are always
913 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
915 RegionList new_regions;
919 boost::shared_ptr<Region> region;
920 boost::shared_ptr<Region> current;
922 RegionList::iterator tmp;
923 Evoral::OverlapType overlap;
924 framepos_t pos1, pos2, pos3, pos4;
928 /* need to work from a copy, because otherwise the regions we add during the process
929 get operated on as well.
932 RegionList copy = regions.rlist();
934 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
941 if (current->first_frame() >= start && current->last_frame() < end) {
944 remove_region_internal (current);
950 /* coverage will return OverlapStart if the start coincides
951 with the end point. we do not partition such a region,
952 so catch this special case.
955 if (current->first_frame() >= end) {
959 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
963 pos1 = current->position();
966 pos4 = current->last_frame();
968 if (overlap == Evoral::OverlapInternal) {
969 /* split: we need 3 new regions, the front, middle and end.
970 cut: we need 2 regions, the front and end.
975 ---------------*************************------------
978 ---------------*****++++++++++++++++====------------
980 ---------------*****----------------====------------
985 /* "middle" ++++++ */
987 RegionFactory::region_name (new_name, current->name(), false);
991 plist.add (Properties::start, current->start() + (pos2 - pos1));
992 plist.add (Properties::length, pos3 - pos2);
993 plist.add (Properties::name, new_name);
994 plist.add (Properties::layer, current->layer ());
995 plist.add (Properties::layering_index, current->layering_index ());
996 plist.add (Properties::automatic, true);
997 plist.add (Properties::left_of_split, true);
998 plist.add (Properties::right_of_split, true);
999 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos2 - pos1));
1001 /* see note in :_split_region()
1002 * for MusicFrame is needed to offset region-gain
1004 region = RegionFactory::create (current, MusicFrame (pos2 - pos1, 0), plist);
1005 add_region_internal (region, start);
1006 new_regions.push_back (region);
1011 RegionFactory::region_name (new_name, current->name(), false);
1015 plist.add (Properties::start, current->start() + (pos3 - pos1));
1016 plist.add (Properties::length, pos4 - pos3);
1017 plist.add (Properties::name, new_name);
1018 plist.add (Properties::layer, current->layer ());
1019 plist.add (Properties::layering_index, current->layering_index ());
1020 plist.add (Properties::automatic, true);
1021 plist.add (Properties::right_of_split, true);
1022 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos3 - pos1));
1024 region = RegionFactory::create (current, MusicFrame (pos3 - pos1, 0), plist);
1026 add_region_internal (region, end);
1027 new_regions.push_back (region);
1031 current->clear_changes ();
1032 current->suspend_property_changes ();
1033 thawlist.push_back (current);
1034 current->cut_end (pos2 - 1);
1036 } else if (overlap == Evoral::OverlapEnd) {
1040 ---------------*************************------------
1043 ---------------**************+++++++++++------------
1045 ---------------**************-----------------------
1052 RegionFactory::region_name (new_name, current->name(), false);
1056 plist.add (Properties::start, current->start() + (pos2 - pos1));
1057 plist.add (Properties::length, pos4 - pos2);
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::left_of_split, true);
1063 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos2 - pos1));
1065 region = RegionFactory::create (current, MusicFrame(pos2 - pos1, 0), plist);
1067 add_region_internal (region, start);
1068 new_regions.push_back (region);
1073 current->clear_changes ();
1074 current->suspend_property_changes ();
1075 thawlist.push_back (current);
1076 current->cut_end (pos2 - 1);
1078 } else if (overlap == Evoral::OverlapStart) {
1080 /* split: we need 2 regions: the front and the end.
1081 cut: just trim current to skip the cut area
1086 ---------------*************************------------
1090 ---------------****+++++++++++++++++++++------------
1092 -------------------*********************------------
1098 RegionFactory::region_name (new_name, current->name(), false);
1102 plist.add (Properties::start, current->start());
1103 plist.add (Properties::length, pos3 - pos1);
1104 plist.add (Properties::name, new_name);
1105 plist.add (Properties::layer, current->layer ());
1106 plist.add (Properties::layering_index, current->layering_index ());
1107 plist.add (Properties::automatic, true);
1108 plist.add (Properties::right_of_split, true);
1109 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start());
1111 region = RegionFactory::create (current, plist);
1113 add_region_internal (region, pos1);
1114 new_regions.push_back (region);
1119 current->clear_changes ();
1120 current->suspend_property_changes ();
1121 thawlist.push_back (current);
1122 current->trim_front (pos3);
1123 } else if (overlap == Evoral::OverlapExternal) {
1125 /* split: no split required.
1126 cut: remove the region.
1131 ---------------*************************------------
1135 ---------------*************************------------
1137 ----------------------------------------------------
1142 remove_region_internal (current);
1145 new_regions.push_back (current);
1149 in_partition = false;
1152 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1153 framepos_t wanted_length = end-start;
1154 _end_space = wanted_length - _get_extent().second - _get_extent().first;
1157 boost::shared_ptr<Playlist>
1158 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1160 boost::shared_ptr<Playlist> ret;
1161 boost::shared_ptr<Playlist> pl;
1164 if (ranges.empty()) {
1165 return boost::shared_ptr<Playlist>();
1168 start = ranges.front().start;
1170 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1172 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1174 if (i == ranges.begin()) {
1178 /* paste the next section into the nascent playlist,
1179 offset to reflect the start of the first range we
1183 ret->paste (pl, (*i).start - start, 1.0f, 0);
1190 boost::shared_ptr<Playlist>
1191 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1193 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1194 return cut_copy (pmf, ranges, result_is_hidden);
1197 boost::shared_ptr<Playlist>
1198 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1200 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1201 return cut_copy (pmf, ranges, result_is_hidden);
1204 boost::shared_ptr<Playlist>
1205 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1207 boost::shared_ptr<Playlist> the_copy;
1208 RegionList thawlist;
1211 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1212 string new_name = _name;
1216 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1217 return boost::shared_ptr<Playlist>();
1221 RegionWriteLock rlock (this);
1222 partition_internal (start, start+cnt-1, true, thawlist);
1225 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1226 (*i)->resume_property_changes();
1232 boost::shared_ptr<Playlist>
1233 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1237 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1238 string new_name = _name;
1242 // cnt = min (_get_extent().second - start, cnt); (We need the full range length when copy/pasting in Ripple. Why was this limit here? It's not in CUT... )
1244 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1248 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times, const int32_t sub_num)
1250 times = fabs (times);
1253 RegionReadLock rl2 (other.get());
1255 int itimes = (int) floor (times);
1256 framepos_t pos = position;
1257 framecnt_t const shift = other->_get_extent().second;
1258 layer_t top = top_layer ();
1261 RegionWriteLock rl1 (this);
1263 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1264 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1266 /* put these new regions on top of all existing ones, but preserve
1267 the ordering they had in the original playlist.
1270 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1271 set_layer (copy_of_region, copy_of_region->layer() + top);
1283 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1285 duplicate(region, position, region->length(), times);
1288 /** @param gap from the beginning of the region to the next beginning */
1290 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1292 times = fabs (times);
1294 RegionWriteLock rl (this);
1295 int itimes = (int) floor (times);
1298 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1299 add_region_internal (copy, position);
1300 set_layer (copy, DBL_MAX);
1304 if (floor (times) != times) {
1305 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1307 RegionFactory::region_name (name, region->name(), false);
1312 plist.add (Properties::start, region->start());
1313 plist.add (Properties::length, length);
1314 plist.add (Properties::name, name);
1316 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1317 add_region_internal (sub, position);
1318 set_layer (sub, DBL_MAX);
1323 /** @param gap from the beginning of the region to the next beginning */
1324 /** @param end the first frame that does _not_ contain a duplicated frame */
1326 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1328 RegionWriteLock rl (this);
1330 while (position + region->length() - 1 < end) {
1331 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1332 add_region_internal (copy, position);
1333 set_layer (copy, DBL_MAX);
1337 if (position < end) {
1338 framecnt_t length = min (region->length(), end - position);
1340 RegionFactory::region_name (name, region->name(), false);
1345 plist.add (Properties::start, region->start());
1346 plist.add (Properties::length, length);
1347 plist.add (Properties::name, name);
1349 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1350 add_region_internal (sub, position);
1351 set_layer (sub, DBL_MAX);
1357 Playlist::duplicate_range (AudioRange& range, float times)
1359 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1360 framecnt_t offset = range.end - range.start;
1361 paste (pl, range.start + offset, times, 0);
1365 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1367 if (ranges.empty()) {
1371 framepos_t min_pos = max_framepos;
1372 framepos_t max_pos = 0;
1374 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1377 min_pos = min (min_pos, (*i).start);
1378 max_pos = max (max_pos, (*i).end);
1381 framecnt_t offset = max_pos - min_pos;
1384 int itimes = (int) floor (times);
1386 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1387 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1388 paste (pl, (*i).start + (offset * count), 1.0f, 0);
1395 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1397 RegionWriteLock rlock (this);
1398 RegionList copy (regions.rlist());
1401 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1403 if ((*r)->last_frame() < at) {
1408 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1409 /* intersected region */
1410 if (!move_intersected) {
1415 /* do not move regions glued to music time - that
1416 has to be done separately.
1419 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1420 fixup.push_back (*r);
1424 (*r)->set_position ((*r)->position() + distance);
1427 /* XXX: may not be necessary; Region::post_set should do this, I think */
1428 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1429 (*r)->recompute_position_from_lock_style (0);
1434 Playlist::split (const MusicFrame& at)
1436 RegionWriteLock rlock (this);
1437 RegionList copy (regions.rlist());
1439 /* use a copy since this operation can modify the region list
1442 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1443 _split_region (*r, at);
1448 Playlist::split_region (boost::shared_ptr<Region> region, const MusicFrame& playlist_position)
1450 RegionWriteLock rl (this);
1451 _split_region (region, playlist_position);
1455 Playlist::_split_region (boost::shared_ptr<Region> region, const MusicFrame& playlist_position)
1457 if (!region->covers (playlist_position.frame)) {
1461 if (region->position() == playlist_position.frame ||
1462 region->last_frame() == playlist_position.frame) {
1466 boost::shared_ptr<Region> left;
1467 boost::shared_ptr<Region> right;
1469 MusicFrame before (playlist_position.frame - region->position(), playlist_position.division);
1470 MusicFrame after (region->length() - before.frame, 0);
1474 /* split doesn't change anything about length, so don't try to splice */
1476 bool old_sp = _splicing;
1479 RegionFactory::region_name (before_name, region->name(), false);
1484 plist.add (Properties::length, before.frame);
1485 plist.add (Properties::name, before_name);
1486 plist.add (Properties::left_of_split, true);
1487 plist.add (Properties::layering_index, region->layering_index ());
1488 plist.add (Properties::layer, region->layer ());
1490 /* note: we must use the version of ::create with an offset here,
1491 since it supplies that offset to the Region constructor, which
1492 is necessary to get audio region gain envelopes right.
1494 left = RegionFactory::create (region, MusicFrame (0, 0), plist, true);
1497 RegionFactory::region_name (after_name, region->name(), false);
1502 plist.add (Properties::length, after.frame);
1503 plist.add (Properties::name, after_name);
1504 plist.add (Properties::right_of_split, true);
1505 plist.add (Properties::layering_index, region->layering_index ());
1506 plist.add (Properties::layer, region->layer ());
1508 /* same note as above */
1509 right = RegionFactory::create (region, before, plist, true);
1512 add_region_internal (left, region->position(), 0);
1513 add_region_internal (right, region->position() + before.frame, before.division);
1515 remove_region_internal (region);
1521 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1523 if (_splicing || in_set_state) {
1524 /* don't respond to splicing moves or state setting */
1528 if (_edit_mode == Splice) {
1529 splice_locked (at, distance, exclude);
1534 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1536 if (_splicing || in_set_state) {
1537 /* don't respond to splicing moves or state setting */
1541 if (_edit_mode == Splice) {
1542 splice_unlocked (at, distance, exclude);
1547 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1550 RegionWriteLock rl (this);
1551 core_splice (at, distance, exclude);
1556 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1558 core_splice (at, distance, exclude);
1562 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1566 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1568 if (exclude && (*i) == exclude) {
1572 if ((*i)->position() >= at) {
1573 framepos_t new_pos = (*i)->position() + distance;
1576 } else if (new_pos >= max_framepos - (*i)->length()) {
1577 new_pos = max_framepos - (*i)->length();
1580 (*i)->set_position (new_pos);
1586 notify_contents_changed ();
1590 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1593 RegionWriteLock rl (this);
1594 core_ripple (at, distance, exclude);
1599 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1601 core_ripple (at, distance, exclude);
1605 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1607 if (distance == 0) {
1612 RegionListProperty copy = regions;
1613 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1614 assert (i != copy.end());
1617 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1622 if ((*i)->position() >= at) {
1623 framepos_t new_pos = (*i)->position() + distance;
1624 framepos_t limit = max_framepos - (*i)->length();
1627 } else if (new_pos >= limit ) {
1631 (*i)->set_position (new_pos);
1636 notify_contents_changed ();
1641 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1643 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1647 if (what_changed.contains (Properties::position)) {
1649 /* remove it from the list then add it back in
1650 the right place again.
1653 RegionSortByPosition cmp;
1655 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1657 if (i == regions.end()) {
1658 /* the region bounds are being modified but its not currently
1659 in the region list. we will use its bounds correctly when/if
1666 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1669 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1671 frameoffset_t delta = 0;
1673 if (what_changed.contains (Properties::position)) {
1674 delta = region->position() - region->last_position();
1677 if (what_changed.contains (Properties::length)) {
1678 delta += region->length() - region->last_length();
1682 possibly_splice (region->last_position() + region->last_length(), delta, region);
1685 if (holding_state ()) {
1686 pending_bounds.push_back (region);
1688 notify_contents_changed ();
1690 list<Evoral::Range<framepos_t> > xf;
1691 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1692 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1693 coalesce_and_check_crossfades (xf);
1699 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1701 boost::shared_ptr<Region> region (weak_region.lock());
1707 /* this makes a virtual call to the right kind of playlist ... */
1709 region_changed (what_changed, region);
1713 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1715 PropertyChange our_interests;
1716 PropertyChange bounds;
1717 PropertyChange pos_and_length;
1720 if (in_set_state || in_flush) {
1724 our_interests.add (Properties::muted);
1725 our_interests.add (Properties::layer);
1726 our_interests.add (Properties::opaque);
1728 bounds.add (Properties::start);
1729 bounds.add (Properties::position);
1730 bounds.add (Properties::length);
1732 pos_and_length.add (Properties::position);
1733 pos_and_length.add (Properties::length);
1735 if (what_changed.contains (bounds)) {
1736 region_bounds_changed (what_changed, region);
1737 save = !(_splicing || _nudging);
1740 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1741 notify_region_moved (region);
1742 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1743 notify_region_end_trimmed (region);
1744 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1745 notify_region_start_trimmed (region);
1748 /* don't notify about layer changes, since we are the only object that can initiate
1749 them, and we notify in ::relayer()
1752 if (what_changed.contains (our_interests)) {
1756 mark_session_dirty ();
1762 Playlist::drop_regions ()
1764 RegionWriteLock rl (this);
1766 all_regions.clear ();
1770 Playlist::sync_all_regions_with_regions ()
1772 RegionWriteLock rl (this);
1774 all_regions.clear ();
1776 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1777 all_regions.insert (*i);
1782 Playlist::clear (bool with_signals)
1785 RegionWriteLock rl (this);
1787 region_state_changed_connections.drop_connections ();
1788 region_drop_references_connections.drop_connections ();
1790 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1791 pending_removes.insert (*i);
1796 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1797 remove_dependents (*s);
1803 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1804 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1807 pending_removes.clear ();
1808 pending_contents_change = false;
1814 /* *********************************************************************
1816 **********************************************************************/
1818 boost::shared_ptr<RegionList>
1819 Playlist::region_list()
1821 RegionReadLock rlock (this);
1822 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1827 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1829 RegionReadLock rlock (const_cast<Playlist*>(this));
1831 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1832 (*i)->deep_sources (sources);
1836 boost::shared_ptr<RegionList>
1837 Playlist::regions_at (framepos_t frame)
1839 RegionReadLock rlock (this);
1840 return find_regions_at (frame);
1844 Playlist::count_regions_at (framepos_t frame) const
1846 RegionReadLock rlock (const_cast<Playlist*>(this));
1849 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1850 if ((*i)->covers (frame)) {
1858 boost::shared_ptr<Region>
1859 Playlist::top_region_at (framepos_t frame)
1862 RegionReadLock rlock (this);
1863 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1864 boost::shared_ptr<Region> region;
1866 if (rlist->size()) {
1867 RegionSortByLayer cmp;
1869 region = rlist->back();
1875 boost::shared_ptr<Region>
1876 Playlist::top_unmuted_region_at (framepos_t frame)
1879 RegionReadLock rlock (this);
1880 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1882 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1884 RegionList::iterator tmp = i;
1888 if ((*i)->muted()) {
1895 boost::shared_ptr<Region> region;
1897 if (rlist->size()) {
1898 RegionSortByLayer cmp;
1900 region = rlist->back();
1906 boost::shared_ptr<RegionList>
1907 Playlist::find_regions_at (framepos_t frame)
1909 /* Caller must hold lock */
1911 boost::shared_ptr<RegionList> rlist (new RegionList);
1913 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1914 if ((*i)->covers (frame)) {
1915 rlist->push_back (*i);
1922 boost::shared_ptr<RegionList>
1923 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1925 RegionReadLock rlock (this);
1926 boost::shared_ptr<RegionList> rlist (new RegionList);
1928 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1929 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1930 rlist->push_back (*i);
1937 boost::shared_ptr<RegionList>
1938 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1940 RegionReadLock rlock (this);
1941 boost::shared_ptr<RegionList> rlist (new RegionList);
1943 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1944 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1945 rlist->push_back (*i);
1952 /** @param start Range start.
1953 * @param end Range end.
1954 * @return regions which have some part within this range.
1956 boost::shared_ptr<RegionList>
1957 Playlist::regions_touched (framepos_t start, framepos_t end)
1959 RegionReadLock rlock (this);
1960 return regions_touched_locked (start, end);
1963 boost::shared_ptr<RegionList>
1964 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1966 boost::shared_ptr<RegionList> rlist (new RegionList);
1968 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1969 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1970 rlist->push_back (*i);
1978 Playlist::find_next_transient (framepos_t from, int dir)
1980 RegionReadLock rlock (this);
1981 AnalysisFeatureList points;
1982 AnalysisFeatureList these_points;
1984 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1986 if ((*i)->last_frame() < from) {
1990 if ((*i)->first_frame() > from) {
1995 (*i)->get_transients (these_points);
1997 /* add first frame, just, err, because */
1999 these_points.push_back ((*i)->first_frame());
2001 points.insert (points.end(), these_points.begin(), these_points.end());
2002 these_points.clear ();
2005 if (points.empty()) {
2009 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
2010 bool reached = false;
2013 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
2018 if (reached && (*x) > from) {
2023 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2028 if (reached && (*x) < from) {
2037 boost::shared_ptr<Region>
2038 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2040 RegionReadLock rlock (this);
2041 boost::shared_ptr<Region> ret;
2042 framepos_t closest = max_framepos;
2044 bool end_iter = false;
2046 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2050 frameoffset_t distance;
2051 boost::shared_ptr<Region> r = (*i);
2056 pos = r->first_frame ();
2059 pos = r->last_frame ();
2062 pos = r->sync_position ();
2067 case 1: /* forwards */
2070 if ((distance = pos - frame) < closest) {
2079 default: /* backwards */
2082 if ((distance = frame - pos) < closest) {
2098 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2100 RegionReadLock rlock (this);
2102 framepos_t closest = max_framepos;
2103 framepos_t ret = -1;
2107 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2109 boost::shared_ptr<Region> r = (*i);
2110 frameoffset_t distance;
2111 const framepos_t first_frame = r->first_frame();
2112 const framepos_t last_frame = r->last_frame();
2114 if (first_frame > frame) {
2116 distance = first_frame - frame;
2118 if (distance < closest) {
2124 if (last_frame > frame) {
2126 distance = last_frame - frame;
2128 if (distance < closest) {
2137 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2139 boost::shared_ptr<Region> r = (*i);
2140 frameoffset_t distance;
2141 const framepos_t first_frame = r->first_frame();
2142 const framepos_t last_frame = r->last_frame();
2144 if (last_frame < frame) {
2146 distance = frame - last_frame;
2148 if (distance < closest) {
2154 if (first_frame < frame) {
2156 distance = frame - first_frame;
2158 if (distance < closest) {
2170 /***********************************************************************/
2176 Playlist::mark_session_dirty ()
2178 if (!in_set_state && !holding_state ()) {
2179 _session.set_dirty();
2184 Playlist::rdiff (vector<Command*>& cmds) const
2186 RegionReadLock rlock (const_cast<Playlist *> (this));
2187 Stateful::rdiff (cmds);
2191 Playlist::clear_owned_changes ()
2193 RegionReadLock rlock (this);
2194 Stateful::clear_owned_changes ();
2198 Playlist::update (const RegionListProperty::ChangeRecord& change)
2200 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2201 name(), change.added.size(), change.removed.size()));
2204 /* add the added regions */
2205 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2206 add_region_internal ((*i), (*i)->position());
2208 /* remove the removed regions */
2209 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2217 Playlist::set_state (const XMLNode& node, int version)
2221 XMLNodeConstIterator niter;
2222 XMLPropertyConstIterator piter;
2223 boost::shared_ptr<Region> region;
2225 bool seen_region_nodes = false;
2230 if (node.name() != "Playlist") {
2240 if (node.get_property (X_("name"), name)) {
2245 /* XXX legacy session: fix up later */
2246 node.get_property (X_("orig-diskstream-id"), _orig_track_id);
2248 node.get_property (X_("orig-track-id"), _orig_track_id);
2249 node.get_property (X_("frozen"), _frozen);
2251 node.get_property (X_("combine-ops"), _combine_ops);
2254 if (node.get_property (X_("shared-with-ids"), shared_ids)) {
2255 if (!shared_ids.empty()) {
2256 vector<string> result;
2257 ::split (shared_ids, result, ',');
2258 vector<string>::iterator it = result.begin();
2259 for (; it != result.end(); ++it) {
2260 _shared_with_ids.push_back (PBD::ID(*it));
2267 nlist = node.children();
2269 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2273 if (child->name() == "Region") {
2275 seen_region_nodes = true;
2278 if (!child->get_property ("id", id)) {
2279 error << _("region state node has no ID, ignored") << endmsg;
2283 if ((region = region_by_id (id))) {
2285 region->suspend_property_changes ();
2287 if (region->set_state (*child, version)) {
2288 region->resume_property_changes ();
2292 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2293 region->suspend_property_changes ();
2295 error << _("Playlist: cannot create region from XML") << endmsg;
2300 RegionWriteLock rlock (this);
2301 add_region_internal (region, region->position());
2304 region->resume_property_changes ();
2309 if (seen_region_nodes && regions.empty()) {
2314 notify_contents_changed ();
2317 first_set_state = false;
2323 Playlist::get_state()
2325 return state (true);
2329 Playlist::get_template()
2331 return state (false);
2334 /** @param full_state true to include regions in the returned state, otherwise false.
2337 Playlist::state (bool full_state)
2339 XMLNode *node = new XMLNode (X_("Playlist"));
2341 node->set_property (X_("id"), id());
2342 node->set_property (X_("name"), name());
2343 node->set_property (X_("type"), _type);
2344 node->set_property (X_("orig-track-id"), _orig_track_id);
2347 list<PBD::ID>::const_iterator it = _shared_with_ids.begin();
2348 for (; it != _shared_with_ids.end(); ++it) {
2349 shared_ids += "," + (*it).to_s();
2351 if (!shared_ids.empty()) {
2352 shared_ids.erase(0,1);
2355 node->set_property (X_("shared-with-ids"), shared_ids);
2356 node->set_property (X_("frozen"), _frozen);
2359 RegionReadLock rlock (this);
2361 node->set_property ("combine-ops", _combine_ops);
2363 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2364 node->add_child_nocopy ((*i)->get_state());
2369 node->add_child_copy (*_extra_xml);
2376 Playlist::empty() const
2378 RegionReadLock rlock (const_cast<Playlist *>(this));
2379 return regions.empty();
2383 Playlist::n_regions() const
2385 RegionReadLock rlock (const_cast<Playlist *>(this));
2386 return regions.size();
2389 /** @return true if the all_regions list is empty, ie this playlist
2390 * has never had a region added to it.
2393 Playlist::all_regions_empty() const
2395 RegionReadLock rl (const_cast<Playlist *> (this));
2396 return all_regions.empty();
2399 pair<framepos_t, framepos_t>
2400 Playlist::get_extent () const
2402 RegionReadLock rlock (const_cast<Playlist *>(this));
2403 return _get_extent ();
2406 pair<framepos_t, framepos_t>
2407 Playlist::get_extent_with_endspace () const
2409 pair<framepos_t, framepos_t> l = get_extent();
2410 l.second += _end_space;
2414 pair<framepos_t, framepos_t>
2415 Playlist::_get_extent () const
2417 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2419 if (regions.empty()) {
2424 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2425 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2426 if (e.first < ext.first) {
2427 ext.first = e.first;
2429 if (e.second > ext.second) {
2430 ext.second = e.second;
2438 Playlist::bump_name (string name, Session &session)
2440 string newname = name;
2443 newname = bump_name_once (newname, '.');
2444 } while (session.playlists->by_name (newname)!=NULL);
2451 Playlist::top_layer() const
2453 RegionReadLock rlock (const_cast<Playlist *> (this));
2456 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2457 top = max (top, (*i)->layer());
2463 Playlist::set_edit_mode (EditMode mode)
2468 struct RelayerSort {
2469 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2470 return a->layering_index() < b->layering_index();
2474 /** Set a new layer for a region. This adjusts the layering indices of all
2475 * regions in the playlist to put the specified region in the appropriate
2476 * place. The actual layering will be fixed up when relayer() happens.
2480 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2482 /* Remove the layer we are setting from our region list, and sort it
2483 * using the layer indeces.
2486 RegionList copy = regions.rlist();
2487 copy.remove (region);
2488 copy.sort (RelayerSort ());
2490 /* Put region back in the right place */
2491 RegionList::iterator i = copy.begin();
2492 while (i != copy.end ()) {
2493 if ((*i)->layer() > new_layer) {
2499 copy.insert (i, region);
2501 setup_layering_indices (copy);
2505 Playlist::setup_layering_indices (RegionList const & regions)
2509 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2510 (*k)->set_layering_index (j++);
2514 struct LaterHigherSort {
2515 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2516 return a->position() < b->position();
2520 /** Take the layering indices of each of our regions, compute the layers
2521 * that they should be on, and write the layers back to the regions.
2524 Playlist::relayer ()
2526 /* never compute layers when setting from XML */
2532 /* Build up a new list of regions on each layer, stored in a set of lists
2533 each of which represent some period of time on some layer. The idea
2534 is to avoid having to search the entire region list to establish whether
2535 each region overlaps another */
2537 /* how many pieces to divide this playlist's time up into */
2538 int const divisions = 512;
2540 /* find the start and end positions of the regions on this playlist */
2541 framepos_t start = INT64_MAX;
2543 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2544 start = min (start, (*i)->position());
2545 end = max (end, (*i)->position() + (*i)->length());
2548 /* hence the size of each time division */
2549 double const division_size = (end - start) / double (divisions);
2551 vector<vector<RegionList> > layers;
2552 layers.push_back (vector<RegionList> (divisions));
2554 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2555 RegionList copy = regions.rlist();
2556 switch (Config->get_layer_model()) {
2558 copy.sort (LaterHigherSort ());
2561 copy.sort (RelayerSort ());
2565 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2566 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2567 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2570 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2572 /* find the time divisions that this region covers; if there are no regions on the list,
2573 division_size will equal 0 and in this case we'll just say that
2574 start_division = end_division = 0.
2576 int start_division = 0;
2577 int end_division = 0;
2579 if (division_size > 0) {
2580 start_division = floor ( ((*i)->position() - start) / division_size);
2581 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2582 if (end_division == divisions) {
2587 assert (divisions == 0 || end_division < divisions);
2589 /* find the lowest layer that this region can go on */
2590 size_t j = layers.size();
2592 /* try layer j - 1; it can go on if it overlaps no other region
2593 that is already on that layer
2596 bool overlap = false;
2597 for (int k = start_division; k <= end_division; ++k) {
2598 RegionList::iterator l = layers[j-1][k].begin ();
2599 while (l != layers[j-1][k].end()) {
2600 if ((*l)->overlap_equivalent (*i)) {
2613 /* overlap, so we must use layer j */
2620 if (j == layers.size()) {
2621 /* we need a new layer for this region */
2622 layers.push_back (vector<RegionList> (divisions));
2625 /* put a reference to this region in each of the divisions that it exists in */
2626 for (int k = start_division; k <= end_division; ++k) {
2627 layers[j][k].push_back (*i);
2630 (*i)->set_layer (j);
2633 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2634 relayering because we just removed the only region on the top layer, nothing will
2635 appear to have changed, but the StreamView must still sort itself out. We could
2636 probably keep a note of the top layer last time we relayered, and check that,
2637 but premature optimisation &c...
2639 notify_layering_changed ();
2641 /* This relayer() may have been called as a result of a region removal, in which
2642 case we need to setup layering indices to account for the one that has just
2645 setup_layering_indices (copy);
2649 Playlist::raise_region (boost::shared_ptr<Region> region)
2651 set_layer (region, region->layer() + 1.5);
2656 Playlist::lower_region (boost::shared_ptr<Region> region)
2658 set_layer (region, region->layer() - 1.5);
2663 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2665 set_layer (region, DBL_MAX);
2670 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2672 set_layer (region, -0.5);
2677 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2679 RegionList::iterator i;
2685 RegionWriteLock rlock (const_cast<Playlist *> (this));
2687 for (i = regions.begin(); i != regions.end(); ++i) {
2689 if ((*i)->position() >= start) {
2695 if ((*i)->last_frame() > max_framepos - distance) {
2696 new_pos = max_framepos - (*i)->length();
2698 new_pos = (*i)->position() + distance;
2703 if ((*i)->position() > distance) {
2704 new_pos = (*i)->position() - distance;
2710 (*i)->set_position (new_pos);
2718 notify_contents_changed ();
2724 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2726 RegionReadLock rlock (const_cast<Playlist*> (this));
2728 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2729 /* Note: passing the second argument as false can cause at best
2730 incredibly deep and time-consuming recursion, and at worst
2731 cycles if the user has managed to create cycles of reference
2732 between compound regions. We generally only this during
2733 cleanup, and @param shallow is passed as true.
2735 if ((*r)->uses_source (src, shallow)) {
2744 boost::shared_ptr<Region>
2745 Playlist::find_region (const ID& id) const
2747 RegionReadLock rlock (const_cast<Playlist*> (this));
2749 /* searches all regions currently in use by the playlist */
2751 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2752 if ((*i)->id() == id) {
2757 return boost::shared_ptr<Region> ();
2761 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2763 RegionReadLock rlock (const_cast<Playlist*> (this));
2766 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2772 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2773 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2774 /* check if region is used in a compound */
2775 if (it->second == r) {
2776 /* region is referenced as 'original' of a compound */
2780 if (r->whole_file() && r->max_source_level() > 0) {
2781 /* region itself ia a compound.
2782 * the compound regions are not referenced -> check regions inside compound
2784 const SourceList& sl = r->sources();
2785 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2786 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2788 if (ps->playlist()->region_use_count(it->first)) {
2789 // break out of both loops
2798 boost::shared_ptr<Region>
2799 Playlist::region_by_id (const ID& id) const
2801 /* searches all regions ever added to this playlist */
2803 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2804 if ((*i)->id() == id) {
2808 return boost::shared_ptr<Region> ();
2812 Playlist::dump () const
2814 boost::shared_ptr<Region> r;
2816 cerr << "Playlist \"" << _name << "\" " << endl
2817 << regions.size() << " regions "
2820 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2822 cerr << " " << r->name() << " ["
2823 << r->start() << "+" << r->length()
2833 Playlist::set_frozen (bool yn)
2839 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2843 if (region->locked()) {
2850 RegionWriteLock rlock (const_cast<Playlist*> (this));
2855 RegionList::iterator next;
2857 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2858 if ((*i) == region) {
2862 if (next != regions.end()) {
2864 if ((*next)->locked()) {
2870 if ((*next)->position() != region->last_frame() + 1) {
2871 /* they didn't used to touch, so after shuffle,
2872 just have them swap positions.
2874 new_pos = (*next)->position();
2876 /* they used to touch, so after shuffle,
2877 make sure they still do. put the earlier
2878 region where the later one will end after
2881 new_pos = region->position() + (*next)->length();
2884 (*next)->set_position (region->position());
2885 region->set_position (new_pos);
2887 /* avoid a full sort */
2889 regions.erase (i); // removes the region from the list */
2891 regions.insert (next, region); // adds it back after next
2900 RegionList::iterator prev = regions.end();
2902 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2903 if ((*i) == region) {
2905 if (prev != regions.end()) {
2907 if ((*prev)->locked()) {
2912 if (region->position() != (*prev)->last_frame() + 1) {
2913 /* they didn't used to touch, so after shuffle,
2914 just have them swap positions.
2916 new_pos = region->position();
2918 /* they used to touch, so after shuffle,
2919 make sure they still do. put the earlier
2920 one where the later one will end after
2922 new_pos = (*prev)->position() + region->length();
2925 region->set_position ((*prev)->position());
2926 (*prev)->set_position (new_pos);
2928 /* avoid a full sort */
2930 regions.erase (i); // remove region
2931 regions.insert (prev, region); // insert region before prev
2947 notify_contents_changed();
2953 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2955 RegionReadLock rlock (const_cast<Playlist*> (this));
2957 if (regions.size() > 1) {
2965 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2967 ripple_locked (at, distance, exclude);
2971 Playlist::update_after_tempo_map_change ()
2973 RegionWriteLock rlock (const_cast<Playlist*> (this));
2974 RegionList copy (regions.rlist());
2978 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2979 (*i)->update_after_tempo_map_change ();
2981 /* possibly causes a contents changed notification (flush_notifications()) */
2986 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2988 RegionReadLock rl (this);
2989 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2995 Playlist::has_region_at (framepos_t const p) const
2997 RegionReadLock (const_cast<Playlist *> (this));
2999 RegionList::const_iterator i = regions.begin ();
3000 while (i != regions.end() && !(*i)->covers (p)) {
3004 return (i != regions.end());
3007 /** Look from a session frame time and find the start time of the next region
3008 * which is on the top layer of this playlist.
3009 * @param t Time to look from.
3010 * @return Position of next top-layered region, or max_framepos if there isn't one.
3013 Playlist::find_next_top_layer_position (framepos_t t) const
3015 RegionReadLock rlock (const_cast<Playlist *> (this));
3017 layer_t const top = top_layer ();
3019 RegionList copy = regions.rlist ();
3020 copy.sort (RegionSortByPosition ());
3022 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3023 if ((*i)->position() >= t && (*i)->layer() == top) {
3024 return (*i)->position();
3028 return max_framepos;
3031 boost::shared_ptr<Region>
3032 Playlist::combine (const RegionList& r)
3035 uint32_t channels = 0;
3037 framepos_t earliest_position = max_framepos;
3038 vector<TwoRegions> old_and_new_regions;
3039 vector<boost::shared_ptr<Region> > originals;
3040 vector<boost::shared_ptr<Region> > copies;
3043 uint32_t max_level = 0;
3045 /* find the maximum depth of all the regions we're combining */
3047 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3048 max_level = max (max_level, (*i)->max_source_level());
3051 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3052 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3054 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3056 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3057 earliest_position = min (earliest_position, (*i)->position());
3060 /* enable this so that we do not try to create xfades etc. as we add
3064 pl->in_partition = true;
3066 /* sort by position then layer.
3067 * route_time_axis passes 'selected_regions' - which is not sorted.
3068 * here we need the top-most first, then every layer's region sorted by position.
3070 RegionList sorted(r);
3071 sorted.sort(RegionSortByLayerAndPosition());
3073 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3075 /* copy the region */
3077 boost::shared_ptr<Region> original_region = (*i);
3078 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3080 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3081 originals.push_back (original_region);
3082 copies.push_back (copied_region);
3084 RegionFactory::add_compound_association (original_region, copied_region);
3086 /* make position relative to zero */
3088 pl->add_region (copied_region, original_region->position() - earliest_position);
3089 copied_region->set_layer (original_region->layer ());
3091 /* use the maximum number of channels for any region */
3093 channels = max (channels, original_region->n_channels());
3095 /* it will go above the layer of the highest existing region */
3097 layer = max (layer, original_region->layer());
3100 pl->in_partition = false;
3102 pre_combine (copies);
3104 /* now create a new PlaylistSource for each channel in the new playlist */
3107 pair<framepos_t,framepos_t> extent = pl->get_extent();
3109 for (uint32_t chn = 0; chn < channels; ++chn) {
3110 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3114 /* now a new whole-file region using the list of sources */
3116 plist.add (Properties::start, 0);
3117 plist.add (Properties::length, extent.second);
3118 plist.add (Properties::name, parent_name);
3119 plist.add (Properties::whole_file, true);
3121 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3123 /* now the non-whole-file region that we will actually use in the
3128 plist.add (Properties::start, 0);
3129 plist.add (Properties::length, extent.second);
3130 plist.add (Properties::name, child_name);
3131 plist.add (Properties::layer, layer+1);
3133 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3135 /* remove all the selected regions from the current playlist
3140 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3144 /* do type-specific stuff with the originals and the new compound
3148 post_combine (originals, compound_region);
3150 /* add the new region at the right location */
3152 add_region (compound_region, earliest_position);
3158 return compound_region;
3162 Playlist::uncombine (boost::shared_ptr<Region> target)
3164 boost::shared_ptr<PlaylistSource> pls;
3165 boost::shared_ptr<const Playlist> pl;
3166 vector<boost::shared_ptr<Region> > originals;
3167 vector<TwoRegions> old_and_new_regions;
3169 // (1) check that its really a compound region
3171 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3175 pl = pls->playlist();
3177 framepos_t adjusted_start = 0; // gcc isn't smart enough
3178 framepos_t adjusted_end = 0; // gcc isn't smart enough
3180 /* the leftmost (earliest) edge of the compound region
3181 starts at zero in its source, or larger if it
3182 has been trimmed or content-scrolled.
3184 the rightmost (latest) edge of the compound region
3185 relative to its source is the starting point plus
3186 the length of the region.
3189 // (2) get all the original regions
3191 const RegionList& rl (pl->region_list_property().rlist());
3192 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3193 frameoffset_t move_offset = 0;
3195 /* there are two possibilities here:
3196 1) the playlist that the playlist source was based on
3197 is us, so just add the originals (which belonged to
3198 us anyway) back in the right place.
3200 2) the playlist that the playlist source was based on
3201 is NOT us, so we need to make copies of each of
3202 the original regions that we find, and add them
3205 bool same_playlist = (pls->original() == id());
3207 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3209 boost::shared_ptr<Region> current (*i);
3211 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3213 if (ca == cassocs.end()) {
3217 boost::shared_ptr<Region> original (ca->second);
3219 bool modified_region;
3221 if (i == rl.begin()) {
3222 move_offset = (target->position() - original->position()) - target->start();
3223 adjusted_start = original->position() + target->start();
3224 adjusted_end = adjusted_start + target->length();
3227 if (!same_playlist) {
3228 framepos_t pos = original->position();
3229 /* make a copy, but don't announce it */
3230 original = RegionFactory::create (original, false);
3231 /* the pure copy constructor resets position() to zero,
3234 original->set_position (pos);
3237 /* check to see how the original region (in the
3238 * playlist before compounding occurred) overlaps
3239 * with the new state of the compound region.
3242 original->clear_changes ();
3243 modified_region = false;
3245 switch (original->coverage (adjusted_start, adjusted_end)) {
3246 case Evoral::OverlapNone:
3247 /* original region does not cover any part
3248 of the current state of the compound region
3252 case Evoral::OverlapInternal:
3253 /* overlap is just a small piece inside the
3254 * original so trim both ends
3256 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3257 modified_region = true;
3260 case Evoral::OverlapExternal:
3261 /* overlap fully covers original, so leave it
3266 case Evoral::OverlapEnd:
3267 /* overlap starts within but covers end,
3268 so trim the front of the region
3270 original->trim_front (adjusted_start);
3271 modified_region = true;
3274 case Evoral::OverlapStart:
3275 /* overlap covers start but ends within, so
3276 * trim the end of the region.
3278 original->trim_end (adjusted_end);
3279 modified_region = true;
3284 /* fix the position to match any movement of the compound region.
3286 original->set_position (original->position() + move_offset);
3287 modified_region = true;
3290 if (modified_region) {
3291 _session.add_command (new StatefulDiffCommand (original));
3294 /* and add to the list of regions waiting to be
3298 originals.push_back (original);
3299 old_and_new_regions.push_back (TwoRegions (*i, original));
3302 pre_uncombine (originals, target);
3304 in_partition = true;
3307 // (3) remove the compound region
3309 remove_region (target);
3311 // (4) add the constituent regions
3313 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3314 add_region ((*i), (*i)->position());
3315 set_layer((*i), (*i)->layer());
3316 if (!RegionFactory::region_by_id((*i)->id())) {
3317 RegionFactory::map_add(*i);
3321 in_partition = false;
3326 Playlist::fade_range (list<AudioRange>& ranges)
3328 RegionReadLock rlock (this);
3329 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ) {
3330 list<AudioRange>::iterator tmpr = r;
3332 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ) {
3333 RegionList::const_iterator tmpi = i;
3335 (*i)->fade_range ((*r).start, (*r).end);
3343 Playlist::max_source_level () const
3345 RegionReadLock rlock (const_cast<Playlist *> (this));
3348 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3349 lvl = max (lvl, (*i)->max_source_level());
3356 Playlist::set_orig_track_id (const PBD::ID& id)
3358 if (shared_with(id)) {
3359 // Swap 'shared_id' / origin_track_id
3361 share_with (_orig_track_id);
3363 _orig_track_id = id;
3367 Playlist::share_with (const PBD::ID& id)
3369 if (!shared_with(id)) {
3370 _shared_with_ids.push_back (id);
3375 Playlist::unshare_with (const PBD::ID& id)
3377 list<PBD::ID>::iterator it = _shared_with_ids.begin ();
3378 while (it != _shared_with_ids.end()) {
3380 _shared_with_ids.erase (it);
3388 Playlist::shared_with (const PBD::ID& id) const
3390 bool shared = false;
3391 list<PBD::ID>::const_iterator it = _shared_with_ids.begin ();
3392 while (it != _shared_with_ids.end() && !shared) {
3403 Playlist::reset_shares ()
3405 _shared_with_ids.clear();
3408 /** Take a list of ranges, coalesce any that can be coalesced, then call
3409 * check_crossfades for each one.
3412 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3414 /* XXX: it's a shame that this coalesce algorithm also exists in
3415 TimeSelection::consolidate().
3418 /* XXX: xfade: this is implemented in Evoral::RangeList */
3421 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3422 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3428 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3429 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3430 i->from = min (i->from, j->from);
3431 i->to = max (i->to, j->to);
3440 Playlist::set_capture_insertion_in_progress (bool yn)
3442 _capture_insertion_underway = yn;