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, samplepos_t start, samplecnt_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 samplepos_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 sampleoffset_t offset = 0;
210 samplepos_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<samplepos_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<samplepos_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<samplepos_t> const extra (r->position(), r->last_position());
510 if (holding_state ()) {
512 pending_region_extensions.push_back (extra);
516 list<Evoral::Range<samplepos_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<samplepos_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<samplepos_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<samplepos_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, samplepos_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 samplepos_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 samplecnt_t length = 0;
707 if (floor (times) != times) {
708 length = (samplecnt_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, samplepos_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, samplepos_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 samplepos_t pos = (*i)->position();
820 samplecnt_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 (samplepos_t start, samplepos_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, samplepos_t start, samplepos_t end)
899 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(r);
903 double delta_beats = tm.quarter_notes_between_samples (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 (samplepos_t start, samplepos_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 samplepos_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_sample() >= start && current->last_sample() < 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_sample() >= end) {
959 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
963 pos1 = current->position();
966 pos4 = current->last_sample();
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 MusicSample is needed to offset region-gain
1004 region = RegionFactory::create (current, MusicSample (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, MusicSample (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, MusicSample(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 samplepos_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)(samplepos_t, samplecnt_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)(samplepos_t,samplecnt_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)(samplepos_t,samplecnt_t,bool) = &Playlist::copy;
1201 return cut_copy (pmf, ranges, result_is_hidden);
1204 boost::shared_ptr<Playlist>
1205 Playlist::cut (samplepos_t start, samplecnt_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 (samplepos_t start, samplecnt_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, samplepos_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 samplepos_t pos = position;
1257 samplecnt_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, samplepos_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, samplepos_t position, samplecnt_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 samplecnt_t length = (samplecnt_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 sample that does _not_ contain a duplicated sample */
1326 Playlist::duplicate_until (boost::shared_ptr<Region> region, samplepos_t position, samplecnt_t gap, samplepos_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 samplecnt_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 samplecnt_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 samplepos_t min_pos = max_samplepos;
1372 samplepos_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 samplecnt_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 (samplepos_t at, sampleoffset_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_sample() < at) {
1408 if (at > (*r)->first_sample() && at < (*r)->last_sample()) {
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 MusicSample& 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 MusicSample& playlist_position)
1450 RegionWriteLock rl (this);
1451 _split_region (region, playlist_position);
1455 Playlist::_split_region (boost::shared_ptr<Region> region, const MusicSample& playlist_position)
1457 if (!region->covers (playlist_position.sample)) {
1461 if (region->position() == playlist_position.sample ||
1462 region->last_sample() == playlist_position.sample) {
1466 boost::shared_ptr<Region> left;
1467 boost::shared_ptr<Region> right;
1469 MusicSample before (playlist_position.sample - region->position(), playlist_position.division);
1470 MusicSample after (region->length() - before.sample, 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.sample);
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, MusicSample (0, 0), plist, true);
1497 RegionFactory::region_name (after_name, region->name(), false);
1502 plist.add (Properties::length, after.sample);
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.sample, before.division);
1515 remove_region_internal (region);
1521 Playlist::AddToSoloSelectedList(const Region* r)
1523 _soloSelectedRegions.insert (r);
1528 Playlist::RemoveFromSoloSelectedList(const Region* r)
1530 _soloSelectedRegions.erase (r);
1535 Playlist::SoloSelectedListIncludes(const Region* r)
1537 std::set<const Region*>::iterator i = _soloSelectedRegions.find(r);
1539 return ( i != _soloSelectedRegions.end() );
1543 Playlist::SoloSelectedActive()
1545 return !_soloSelectedRegions.empty();
1550 Playlist::possibly_splice (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1552 if (_splicing || in_set_state) {
1553 /* don't respond to splicing moves or state setting */
1557 if (_edit_mode == Splice) {
1558 splice_locked (at, distance, exclude);
1563 Playlist::possibly_splice_unlocked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1565 if (_splicing || in_set_state) {
1566 /* don't respond to splicing moves or state setting */
1570 if (_edit_mode == Splice) {
1571 splice_unlocked (at, distance, exclude);
1576 Playlist::splice_locked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1579 RegionWriteLock rl (this);
1580 core_splice (at, distance, exclude);
1585 Playlist::splice_unlocked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1587 core_splice (at, distance, exclude);
1591 Playlist::core_splice (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1595 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1597 if (exclude && (*i) == exclude) {
1601 if ((*i)->position() >= at) {
1602 samplepos_t new_pos = (*i)->position() + distance;
1605 } else if (new_pos >= max_samplepos - (*i)->length()) {
1606 new_pos = max_samplepos - (*i)->length();
1609 (*i)->set_position (new_pos);
1615 notify_contents_changed ();
1619 Playlist::ripple_locked (samplepos_t at, samplecnt_t distance, RegionList *exclude)
1622 RegionWriteLock rl (this);
1623 core_ripple (at, distance, exclude);
1628 Playlist::ripple_unlocked (samplepos_t at, samplecnt_t distance, RegionList *exclude)
1630 core_ripple (at, distance, exclude);
1634 Playlist::core_ripple (samplepos_t at, samplecnt_t distance, RegionList *exclude)
1636 if (distance == 0) {
1641 RegionListProperty copy = regions;
1642 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1643 assert (i != copy.end());
1646 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1651 if ((*i)->position() >= at) {
1652 samplepos_t new_pos = (*i)->position() + distance;
1653 samplepos_t limit = max_samplepos - (*i)->length();
1656 } else if (new_pos >= limit ) {
1660 (*i)->set_position (new_pos);
1665 notify_contents_changed ();
1670 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1672 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1676 if (what_changed.contains (Properties::position)) {
1678 /* remove it from the list then add it back in
1679 the right place again.
1682 RegionSortByPosition cmp;
1684 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1686 if (i == regions.end()) {
1687 /* the region bounds are being modified but its not currently
1688 in the region list. we will use its bounds correctly when/if
1695 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1698 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1700 sampleoffset_t delta = 0;
1702 if (what_changed.contains (Properties::position)) {
1703 delta = region->position() - region->last_position();
1706 if (what_changed.contains (Properties::length)) {
1707 delta += region->length() - region->last_length();
1711 possibly_splice (region->last_position() + region->last_length(), delta, region);
1714 if (holding_state ()) {
1715 pending_bounds.push_back (region);
1717 notify_contents_changed ();
1719 list<Evoral::Range<samplepos_t> > xf;
1720 xf.push_back (Evoral::Range<samplepos_t> (region->last_range()));
1721 xf.push_back (Evoral::Range<samplepos_t> (region->range()));
1722 coalesce_and_check_crossfades (xf);
1728 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1730 boost::shared_ptr<Region> region (weak_region.lock());
1736 /* this makes a virtual call to the right kind of playlist ... */
1738 region_changed (what_changed, region);
1742 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1744 PropertyChange our_interests;
1745 PropertyChange bounds;
1746 PropertyChange pos_and_length;
1749 if (in_set_state || in_flush) {
1753 our_interests.add (Properties::muted);
1754 our_interests.add (Properties::layer);
1755 our_interests.add (Properties::opaque);
1757 bounds.add (Properties::start);
1758 bounds.add (Properties::position);
1759 bounds.add (Properties::length);
1761 pos_and_length.add (Properties::position);
1762 pos_and_length.add (Properties::length);
1764 if (what_changed.contains (bounds)) {
1765 region_bounds_changed (what_changed, region);
1766 save = !(_splicing || _nudging);
1769 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1770 notify_region_moved (region);
1771 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1772 notify_region_end_trimmed (region);
1773 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1774 notify_region_start_trimmed (region);
1777 /* don't notify about layer changes, since we are the only object that can initiate
1778 them, and we notify in ::relayer()
1781 if (what_changed.contains (our_interests)) {
1785 mark_session_dirty ();
1791 Playlist::drop_regions ()
1793 RegionWriteLock rl (this);
1795 all_regions.clear ();
1799 Playlist::sync_all_regions_with_regions ()
1801 RegionWriteLock rl (this);
1803 all_regions.clear ();
1805 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1806 all_regions.insert (*i);
1811 Playlist::clear (bool with_signals)
1814 RegionWriteLock rl (this);
1816 region_state_changed_connections.drop_connections ();
1817 region_drop_references_connections.drop_connections ();
1819 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1820 pending_removes.insert (*i);
1825 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1826 remove_dependents (*s);
1832 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1833 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1836 pending_removes.clear ();
1837 pending_contents_change = false;
1843 /* *********************************************************************
1845 **********************************************************************/
1847 boost::shared_ptr<RegionList>
1848 Playlist::region_list()
1850 RegionReadLock rlock (this);
1851 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1856 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1858 RegionReadLock rlock (const_cast<Playlist*>(this));
1860 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1861 (*i)->deep_sources (sources);
1865 boost::shared_ptr<RegionList>
1866 Playlist::regions_at (samplepos_t sample)
1868 RegionReadLock rlock (this);
1869 return find_regions_at (sample);
1873 Playlist::count_regions_at (samplepos_t sample) const
1875 RegionReadLock rlock (const_cast<Playlist*>(this));
1878 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1879 if ((*i)->covers (sample)) {
1887 boost::shared_ptr<Region>
1888 Playlist::top_region_at (samplepos_t sample)
1891 RegionReadLock rlock (this);
1892 boost::shared_ptr<RegionList> rlist = find_regions_at (sample);
1893 boost::shared_ptr<Region> region;
1895 if (rlist->size()) {
1896 RegionSortByLayer cmp;
1898 region = rlist->back();
1904 boost::shared_ptr<Region>
1905 Playlist::top_unmuted_region_at (samplepos_t sample)
1908 RegionReadLock rlock (this);
1909 boost::shared_ptr<RegionList> rlist = find_regions_at (sample);
1911 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1913 RegionList::iterator tmp = i;
1917 if ((*i)->muted()) {
1924 boost::shared_ptr<Region> region;
1926 if (rlist->size()) {
1927 RegionSortByLayer cmp;
1929 region = rlist->back();
1935 boost::shared_ptr<RegionList>
1936 Playlist::find_regions_at (samplepos_t sample)
1938 /* Caller must hold lock */
1940 boost::shared_ptr<RegionList> rlist (new RegionList);
1942 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1943 if ((*i)->covers (sample)) {
1944 rlist->push_back (*i);
1951 boost::shared_ptr<RegionList>
1952 Playlist::regions_with_start_within (Evoral::Range<samplepos_t> range)
1954 RegionReadLock rlock (this);
1955 boost::shared_ptr<RegionList> rlist (new RegionList);
1957 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1958 if ((*i)->first_sample() >= range.from && (*i)->first_sample() <= range.to) {
1959 rlist->push_back (*i);
1966 boost::shared_ptr<RegionList>
1967 Playlist::regions_with_end_within (Evoral::Range<samplepos_t> range)
1969 RegionReadLock rlock (this);
1970 boost::shared_ptr<RegionList> rlist (new RegionList);
1972 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1973 if ((*i)->last_sample() >= range.from && (*i)->last_sample() <= range.to) {
1974 rlist->push_back (*i);
1981 /** @param start Range start.
1982 * @param end Range end.
1983 * @return regions which have some part within this range.
1985 boost::shared_ptr<RegionList>
1986 Playlist::regions_touched (samplepos_t start, samplepos_t end)
1988 RegionReadLock rlock (this);
1989 return regions_touched_locked (start, end);
1992 boost::shared_ptr<RegionList>
1993 Playlist::regions_touched_locked (samplepos_t start, samplepos_t end)
1995 boost::shared_ptr<RegionList> rlist (new RegionList);
1997 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1998 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1999 rlist->push_back (*i);
2007 Playlist::find_next_transient (samplepos_t from, int dir)
2009 RegionReadLock rlock (this);
2010 AnalysisFeatureList points;
2011 AnalysisFeatureList these_points;
2013 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2015 if ((*i)->last_sample() < from) {
2019 if ((*i)->first_sample() > from) {
2024 (*i)->get_transients (these_points);
2026 /* add first sample, just, err, because */
2028 these_points.push_back ((*i)->first_sample());
2030 points.insert (points.end(), these_points.begin(), these_points.end());
2031 these_points.clear ();
2034 if (points.empty()) {
2038 TransientDetector::cleanup_transients (points, _session.sample_rate(), 3.0);
2039 bool reached = false;
2042 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
2047 if (reached && (*x) > from) {
2052 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2057 if (reached && (*x) < from) {
2066 boost::shared_ptr<Region>
2067 Playlist::find_next_region (samplepos_t sample, RegionPoint point, int dir)
2069 RegionReadLock rlock (this);
2070 boost::shared_ptr<Region> ret;
2071 samplepos_t closest = max_samplepos;
2073 bool end_iter = false;
2075 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2079 sampleoffset_t distance;
2080 boost::shared_ptr<Region> r = (*i);
2081 samplepos_t pos = 0;
2085 pos = r->first_sample ();
2088 pos = r->last_sample ();
2091 pos = r->sync_position ();
2096 case 1: /* forwards */
2099 if ((distance = pos - sample) < closest) {
2108 default: /* backwards */
2111 if ((distance = sample - pos) < closest) {
2127 Playlist::find_next_region_boundary (samplepos_t sample, int dir)
2129 RegionReadLock rlock (this);
2131 samplepos_t closest = max_samplepos;
2132 samplepos_t ret = -1;
2136 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2138 boost::shared_ptr<Region> r = (*i);
2139 sampleoffset_t distance;
2140 const samplepos_t first_sample = r->first_sample();
2141 const samplepos_t last_sample = r->last_sample();
2143 if (first_sample > sample) {
2145 distance = first_sample - sample;
2147 if (distance < closest) {
2153 if (last_sample > sample) {
2155 distance = last_sample - sample;
2157 if (distance < closest) {
2166 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2168 boost::shared_ptr<Region> r = (*i);
2169 sampleoffset_t distance;
2170 const samplepos_t first_sample = r->first_sample();
2171 const samplepos_t last_sample = r->last_sample();
2173 if (last_sample < sample) {
2175 distance = sample - last_sample;
2177 if (distance < closest) {
2183 if (first_sample < sample) {
2185 distance = sample - first_sample;
2187 if (distance < closest) {
2199 /***********************************************************************/
2205 Playlist::mark_session_dirty ()
2207 if (!in_set_state && !holding_state ()) {
2208 _session.set_dirty();
2213 Playlist::rdiff (vector<Command*>& cmds) const
2215 RegionReadLock rlock (const_cast<Playlist *> (this));
2216 Stateful::rdiff (cmds);
2220 Playlist::clear_owned_changes ()
2222 RegionReadLock rlock (this);
2223 Stateful::clear_owned_changes ();
2227 Playlist::update (const RegionListProperty::ChangeRecord& change)
2229 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2230 name(), change.added.size(), change.removed.size()));
2233 /* add the added regions */
2234 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2235 add_region_internal ((*i), (*i)->position());
2237 /* remove the removed regions */
2238 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2246 Playlist::set_state (const XMLNode& node, int version)
2250 XMLNodeConstIterator niter;
2251 XMLPropertyConstIterator piter;
2252 boost::shared_ptr<Region> region;
2254 bool seen_region_nodes = false;
2259 if (node.name() != "Playlist") {
2269 if (node.get_property (X_("name"), name)) {
2274 /* XXX legacy session: fix up later */
2275 node.get_property (X_("orig-diskstream-id"), _orig_track_id);
2277 node.get_property (X_("orig-track-id"), _orig_track_id);
2278 node.get_property (X_("frozen"), _frozen);
2280 node.get_property (X_("combine-ops"), _combine_ops);
2283 if (node.get_property (X_("shared-with-ids"), shared_ids)) {
2284 if (!shared_ids.empty()) {
2285 vector<string> result;
2286 ::split (shared_ids, result, ',');
2287 vector<string>::iterator it = result.begin();
2288 for (; it != result.end(); ++it) {
2289 _shared_with_ids.push_back (PBD::ID(*it));
2296 nlist = node.children();
2298 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2302 if (child->name() == "Region") {
2304 seen_region_nodes = true;
2307 if (!child->get_property ("id", id)) {
2308 error << _("region state node has no ID, ignored") << endmsg;
2312 if ((region = region_by_id (id))) {
2314 region->suspend_property_changes ();
2316 if (region->set_state (*child, version)) {
2317 region->resume_property_changes ();
2321 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2322 region->suspend_property_changes ();
2324 error << _("Playlist: cannot create region from XML") << endmsg;
2329 RegionWriteLock rlock (this);
2330 add_region_internal (region, region->position());
2333 region->resume_property_changes ();
2338 if (seen_region_nodes && regions.empty()) {
2343 notify_contents_changed ();
2346 first_set_state = false;
2352 Playlist::get_state()
2354 return state (true);
2358 Playlist::get_template()
2360 return state (false);
2363 /** @param full_state true to include regions in the returned state, otherwise false.
2366 Playlist::state (bool full_state)
2368 XMLNode *node = new XMLNode (X_("Playlist"));
2370 node->set_property (X_("id"), id());
2371 node->set_property (X_("name"), name());
2372 node->set_property (X_("type"), _type);
2373 node->set_property (X_("orig-track-id"), _orig_track_id);
2376 list<PBD::ID>::const_iterator it = _shared_with_ids.begin();
2377 for (; it != _shared_with_ids.end(); ++it) {
2378 shared_ids += "," + (*it).to_s();
2380 if (!shared_ids.empty()) {
2381 shared_ids.erase(0,1);
2384 node->set_property (X_("shared-with-ids"), shared_ids);
2385 node->set_property (X_("frozen"), _frozen);
2388 RegionReadLock rlock (this);
2390 node->set_property ("combine-ops", _combine_ops);
2392 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2393 node->add_child_nocopy ((*i)->get_state());
2398 node->add_child_copy (*_extra_xml);
2405 Playlist::empty() const
2407 RegionReadLock rlock (const_cast<Playlist *>(this));
2408 return regions.empty();
2412 Playlist::n_regions() const
2414 RegionReadLock rlock (const_cast<Playlist *>(this));
2415 return regions.size();
2418 /** @return true if the all_regions list is empty, ie this playlist
2419 * has never had a region added to it.
2422 Playlist::all_regions_empty() const
2424 RegionReadLock rl (const_cast<Playlist *> (this));
2425 return all_regions.empty();
2428 pair<samplepos_t, samplepos_t>
2429 Playlist::get_extent () const
2431 RegionReadLock rlock (const_cast<Playlist *>(this));
2432 return _get_extent ();
2435 pair<samplepos_t, samplepos_t>
2436 Playlist::get_extent_with_endspace () const
2438 pair<samplepos_t, samplepos_t> l = get_extent();
2439 l.second += _end_space;
2443 pair<samplepos_t, samplepos_t>
2444 Playlist::_get_extent () const
2446 pair<samplepos_t, samplepos_t> ext (max_samplepos, 0);
2448 if (regions.empty()) {
2453 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2454 pair<samplepos_t, samplepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2455 if (e.first < ext.first) {
2456 ext.first = e.first;
2458 if (e.second > ext.second) {
2459 ext.second = e.second;
2467 Playlist::bump_name (string name, Session &session)
2469 string newname = name;
2472 newname = bump_name_once (newname, '.');
2473 } while (session.playlists->by_name (newname)!=NULL);
2480 Playlist::top_layer() const
2482 RegionReadLock rlock (const_cast<Playlist *> (this));
2485 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2486 top = max (top, (*i)->layer());
2492 Playlist::set_edit_mode (EditMode mode)
2497 struct RelayerSort {
2498 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2499 return a->layering_index() < b->layering_index();
2503 /** Set a new layer for a region. This adjusts the layering indices of all
2504 * regions in the playlist to put the specified region in the appropriate
2505 * place. The actual layering will be fixed up when relayer() happens.
2509 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2511 /* Remove the layer we are setting from our region list, and sort it
2512 * using the layer indeces.
2515 RegionList copy = regions.rlist();
2516 copy.remove (region);
2517 copy.sort (RelayerSort ());
2519 /* Put region back in the right place */
2520 RegionList::iterator i = copy.begin();
2521 while (i != copy.end ()) {
2522 if ((*i)->layer() > new_layer) {
2528 copy.insert (i, region);
2530 setup_layering_indices (copy);
2534 Playlist::setup_layering_indices (RegionList const & regions)
2538 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2539 (*k)->set_layering_index (j++);
2543 struct LaterHigherSort {
2544 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2545 return a->position() < b->position();
2549 /** Take the layering indices of each of our regions, compute the layers
2550 * that they should be on, and write the layers back to the regions.
2553 Playlist::relayer ()
2555 /* never compute layers when setting from XML */
2561 /* Build up a new list of regions on each layer, stored in a set of lists
2562 each of which represent some period of time on some layer. The idea
2563 is to avoid having to search the entire region list to establish whether
2564 each region overlaps another */
2566 /* how many pieces to divide this playlist's time up into */
2567 int const divisions = 512;
2569 /* find the start and end positions of the regions on this playlist */
2570 samplepos_t start = INT64_MAX;
2571 samplepos_t end = 0;
2572 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2573 start = min (start, (*i)->position());
2574 end = max (end, (*i)->position() + (*i)->length());
2577 /* hence the size of each time division */
2578 double const division_size = (end - start) / double (divisions);
2580 vector<vector<RegionList> > layers;
2581 layers.push_back (vector<RegionList> (divisions));
2583 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2584 RegionList copy = regions.rlist();
2585 switch (Config->get_layer_model()) {
2587 copy.sort (LaterHigherSort ());
2590 copy.sort (RelayerSort ());
2594 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2595 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2596 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2599 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2601 /* find the time divisions that this region covers; if there are no regions on the list,
2602 division_size will equal 0 and in this case we'll just say that
2603 start_division = end_division = 0.
2605 int start_division = 0;
2606 int end_division = 0;
2608 if (division_size > 0) {
2609 start_division = floor ( ((*i)->position() - start) / division_size);
2610 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2611 if (end_division == divisions) {
2616 assert (divisions == 0 || end_division < divisions);
2618 /* find the lowest layer that this region can go on */
2619 size_t j = layers.size();
2621 /* try layer j - 1; it can go on if it overlaps no other region
2622 that is already on that layer
2625 bool overlap = false;
2626 for (int k = start_division; k <= end_division; ++k) {
2627 RegionList::iterator l = layers[j-1][k].begin ();
2628 while (l != layers[j-1][k].end()) {
2629 if ((*l)->overlap_equivalent (*i)) {
2642 /* overlap, so we must use layer j */
2649 if (j == layers.size()) {
2650 /* we need a new layer for this region */
2651 layers.push_back (vector<RegionList> (divisions));
2654 /* put a reference to this region in each of the divisions that it exists in */
2655 for (int k = start_division; k <= end_division; ++k) {
2656 layers[j][k].push_back (*i);
2659 (*i)->set_layer (j);
2662 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2663 relayering because we just removed the only region on the top layer, nothing will
2664 appear to have changed, but the StreamView must still sort itself out. We could
2665 probably keep a note of the top layer last time we relayered, and check that,
2666 but premature optimisation &c...
2668 notify_layering_changed ();
2670 /* This relayer() may have been called as a result of a region removal, in which
2671 case we need to setup layering indices to account for the one that has just
2674 setup_layering_indices (copy);
2678 Playlist::raise_region (boost::shared_ptr<Region> region)
2680 set_layer (region, region->layer() + 1.5);
2685 Playlist::lower_region (boost::shared_ptr<Region> region)
2687 set_layer (region, region->layer() - 1.5);
2692 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2694 set_layer (region, DBL_MAX);
2699 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2701 set_layer (region, -0.5);
2706 Playlist::nudge_after (samplepos_t start, samplecnt_t distance, bool forwards)
2708 RegionList::iterator i;
2714 RegionWriteLock rlock (const_cast<Playlist *> (this));
2716 for (i = regions.begin(); i != regions.end(); ++i) {
2718 if ((*i)->position() >= start) {
2720 samplepos_t new_pos;
2724 if ((*i)->last_sample() > max_samplepos - distance) {
2725 new_pos = max_samplepos - (*i)->length();
2727 new_pos = (*i)->position() + distance;
2732 if ((*i)->position() > distance) {
2733 new_pos = (*i)->position() - distance;
2739 (*i)->set_position (new_pos);
2747 notify_contents_changed ();
2753 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2755 RegionReadLock rlock (const_cast<Playlist*> (this));
2757 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2758 /* Note: passing the second argument as false can cause at best
2759 incredibly deep and time-consuming recursion, and at worst
2760 cycles if the user has managed to create cycles of reference
2761 between compound regions. We generally only this during
2762 cleanup, and @param shallow is passed as true.
2764 if ((*r)->uses_source (src, shallow)) {
2773 boost::shared_ptr<Region>
2774 Playlist::find_region (const ID& id) const
2776 RegionReadLock rlock (const_cast<Playlist*> (this));
2778 /* searches all regions currently in use by the playlist */
2780 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2781 if ((*i)->id() == id) {
2786 return boost::shared_ptr<Region> ();
2790 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2792 RegionReadLock rlock (const_cast<Playlist*> (this));
2795 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2801 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2802 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2803 /* check if region is used in a compound */
2804 if (it->second == r) {
2805 /* region is referenced as 'original' of a compound */
2809 if (r->whole_file() && r->max_source_level() > 0) {
2810 /* region itself ia a compound.
2811 * the compound regions are not referenced -> check regions inside compound
2813 const SourceList& sl = r->sources();
2814 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2815 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2817 if (ps->playlist()->region_use_count(it->first)) {
2818 // break out of both loops
2827 boost::shared_ptr<Region>
2828 Playlist::region_by_id (const ID& id) const
2830 /* searches all regions ever added to this playlist */
2832 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2833 if ((*i)->id() == id) {
2837 return boost::shared_ptr<Region> ();
2841 Playlist::dump () const
2843 boost::shared_ptr<Region> r;
2845 cerr << "Playlist \"" << _name << "\" " << endl
2846 << regions.size() << " regions "
2849 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2851 cerr << " " << r->name() << " ["
2852 << r->start() << "+" << r->length()
2862 Playlist::set_frozen (bool yn)
2868 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2872 if (region->locked()) {
2879 RegionWriteLock rlock (const_cast<Playlist*> (this));
2884 RegionList::iterator next;
2886 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2887 if ((*i) == region) {
2891 if (next != regions.end()) {
2893 if ((*next)->locked()) {
2897 samplepos_t new_pos;
2899 if ((*next)->position() != region->last_sample() + 1) {
2900 /* they didn't used to touch, so after shuffle,
2901 just have them swap positions.
2903 new_pos = (*next)->position();
2905 /* they used to touch, so after shuffle,
2906 make sure they still do. put the earlier
2907 region where the later one will end after
2910 new_pos = region->position() + (*next)->length();
2913 (*next)->set_position (region->position());
2914 region->set_position (new_pos);
2916 /* avoid a full sort */
2918 regions.erase (i); // removes the region from the list */
2920 regions.insert (next, region); // adds it back after next
2929 RegionList::iterator prev = regions.end();
2931 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2932 if ((*i) == region) {
2934 if (prev != regions.end()) {
2936 if ((*prev)->locked()) {
2940 samplepos_t new_pos;
2941 if (region->position() != (*prev)->last_sample() + 1) {
2942 /* they didn't used to touch, so after shuffle,
2943 just have them swap positions.
2945 new_pos = region->position();
2947 /* they used to touch, so after shuffle,
2948 make sure they still do. put the earlier
2949 one where the later one will end after
2951 new_pos = (*prev)->position() + region->length();
2954 region->set_position ((*prev)->position());
2955 (*prev)->set_position (new_pos);
2957 /* avoid a full sort */
2959 regions.erase (i); // remove region
2960 regions.insert (prev, region); // insert region before prev
2976 notify_contents_changed();
2982 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2984 RegionReadLock rlock (const_cast<Playlist*> (this));
2986 if (regions.size() > 1) {
2994 Playlist::ripple (samplepos_t at, samplecnt_t distance, RegionList *exclude)
2996 ripple_locked (at, distance, exclude);
3000 Playlist::update_after_tempo_map_change ()
3002 RegionWriteLock rlock (const_cast<Playlist*> (this));
3003 RegionList copy (regions.rlist());
3007 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
3008 (*i)->update_after_tempo_map_change ();
3010 /* possibly causes a contents changed notification (flush_notifications()) */
3015 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
3017 RegionReadLock rl (this);
3018 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
3024 Playlist::has_region_at (samplepos_t const p) const
3026 RegionReadLock (const_cast<Playlist *> (this));
3028 RegionList::const_iterator i = regions.begin ();
3029 while (i != regions.end() && !(*i)->covers (p)) {
3033 return (i != regions.end());
3036 /** Look from a session sample time and find the start time of the next region
3037 * which is on the top layer of this playlist.
3038 * @param t Time to look from.
3039 * @return Position of next top-layered region, or max_samplepos if there isn't one.
3042 Playlist::find_next_top_layer_position (samplepos_t t) const
3044 RegionReadLock rlock (const_cast<Playlist *> (this));
3046 layer_t const top = top_layer ();
3048 RegionList copy = regions.rlist ();
3049 copy.sort (RegionSortByPosition ());
3051 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3052 if ((*i)->position() >= t && (*i)->layer() == top) {
3053 return (*i)->position();
3057 return max_samplepos;
3060 boost::shared_ptr<Region>
3061 Playlist::combine (const RegionList& r)
3064 uint32_t channels = 0;
3066 samplepos_t earliest_position = max_samplepos;
3067 vector<TwoRegions> old_and_new_regions;
3068 vector<boost::shared_ptr<Region> > originals;
3069 vector<boost::shared_ptr<Region> > copies;
3072 uint32_t max_level = 0;
3074 /* find the maximum depth of all the regions we're combining */
3076 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3077 max_level = max (max_level, (*i)->max_source_level());
3080 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3081 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3083 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3085 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3086 earliest_position = min (earliest_position, (*i)->position());
3089 /* enable this so that we do not try to create xfades etc. as we add
3093 pl->in_partition = true;
3095 /* sort by position then layer.
3096 * route_time_axis passes 'selected_regions' - which is not sorted.
3097 * here we need the top-most first, then every layer's region sorted by position.
3099 RegionList sorted(r);
3100 sorted.sort(RegionSortByLayerAndPosition());
3102 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3104 /* copy the region */
3106 boost::shared_ptr<Region> original_region = (*i);
3107 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3109 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3110 originals.push_back (original_region);
3111 copies.push_back (copied_region);
3113 RegionFactory::add_compound_association (original_region, copied_region);
3115 /* make position relative to zero */
3117 pl->add_region (copied_region, original_region->position() - earliest_position);
3118 copied_region->set_layer (original_region->layer ());
3120 /* use the maximum number of channels for any region */
3122 channels = max (channels, original_region->n_channels());
3124 /* it will go above the layer of the highest existing region */
3126 layer = max (layer, original_region->layer());
3129 pl->in_partition = false;
3131 pre_combine (copies);
3133 /* now create a new PlaylistSource for each channel in the new playlist */
3136 pair<samplepos_t,samplepos_t> extent = pl->get_extent();
3138 for (uint32_t chn = 0; chn < channels; ++chn) {
3139 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3143 /* now a new whole-file region using the list of sources */
3145 plist.add (Properties::start, 0);
3146 plist.add (Properties::length, extent.second);
3147 plist.add (Properties::name, parent_name);
3148 plist.add (Properties::whole_file, true);
3150 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3152 /* now the non-whole-file region that we will actually use in the
3157 plist.add (Properties::start, 0);
3158 plist.add (Properties::length, extent.second);
3159 plist.add (Properties::name, child_name);
3160 plist.add (Properties::layer, layer+1);
3162 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3164 /* remove all the selected regions from the current playlist
3169 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3173 /* do type-specific stuff with the originals and the new compound
3177 post_combine (originals, compound_region);
3179 /* add the new region at the right location */
3181 add_region (compound_region, earliest_position);
3187 return compound_region;
3191 Playlist::uncombine (boost::shared_ptr<Region> target)
3193 boost::shared_ptr<PlaylistSource> pls;
3194 boost::shared_ptr<const Playlist> pl;
3195 vector<boost::shared_ptr<Region> > originals;
3196 vector<TwoRegions> old_and_new_regions;
3198 // (1) check that its really a compound region
3200 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3204 pl = pls->playlist();
3206 samplepos_t adjusted_start = 0; // gcc isn't smart enough
3207 samplepos_t adjusted_end = 0; // gcc isn't smart enough
3209 /* the leftmost (earliest) edge of the compound region
3210 starts at zero in its source, or larger if it
3211 has been trimmed or content-scrolled.
3213 the rightmost (latest) edge of the compound region
3214 relative to its source is the starting point plus
3215 the length of the region.
3218 // (2) get all the original regions
3220 const RegionList& rl (pl->region_list_property().rlist());
3221 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3222 sampleoffset_t move_offset = 0;
3224 /* there are two possibilities here:
3225 1) the playlist that the playlist source was based on
3226 is us, so just add the originals (which belonged to
3227 us anyway) back in the right place.
3229 2) the playlist that the playlist source was based on
3230 is NOT us, so we need to make copies of each of
3231 the original regions that we find, and add them
3234 bool same_playlist = (pls->original() == id());
3236 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3238 boost::shared_ptr<Region> current (*i);
3240 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3242 if (ca == cassocs.end()) {
3246 boost::shared_ptr<Region> original (ca->second);
3248 bool modified_region;
3250 if (i == rl.begin()) {
3251 move_offset = (target->position() - original->position()) - target->start();
3252 adjusted_start = original->position() + target->start();
3253 adjusted_end = adjusted_start + target->length();
3256 if (!same_playlist) {
3257 samplepos_t pos = original->position();
3258 /* make a copy, but don't announce it */
3259 original = RegionFactory::create (original, false);
3260 /* the pure copy constructor resets position() to zero,
3263 original->set_position (pos);
3266 /* check to see how the original region (in the
3267 * playlist before compounding occurred) overlaps
3268 * with the new state of the compound region.
3271 original->clear_changes ();
3272 modified_region = false;
3274 switch (original->coverage (adjusted_start, adjusted_end)) {
3275 case Evoral::OverlapNone:
3276 /* original region does not cover any part
3277 of the current state of the compound region
3281 case Evoral::OverlapInternal:
3282 /* overlap is just a small piece inside the
3283 * original so trim both ends
3285 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3286 modified_region = true;
3289 case Evoral::OverlapExternal:
3290 /* overlap fully covers original, so leave it
3295 case Evoral::OverlapEnd:
3296 /* overlap starts within but covers end,
3297 so trim the front of the region
3299 original->trim_front (adjusted_start);
3300 modified_region = true;
3303 case Evoral::OverlapStart:
3304 /* overlap covers start but ends within, so
3305 * trim the end of the region.
3307 original->trim_end (adjusted_end);
3308 modified_region = true;
3313 /* fix the position to match any movement of the compound region.
3315 original->set_position (original->position() + move_offset);
3316 modified_region = true;
3319 if (modified_region) {
3320 _session.add_command (new StatefulDiffCommand (original));
3323 /* and add to the list of regions waiting to be
3327 originals.push_back (original);
3328 old_and_new_regions.push_back (TwoRegions (*i, original));
3331 pre_uncombine (originals, target);
3333 in_partition = true;
3336 // (3) remove the compound region
3338 remove_region (target);
3340 // (4) add the constituent regions
3342 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3343 add_region ((*i), (*i)->position());
3344 set_layer((*i), (*i)->layer());
3345 if (!RegionFactory::region_by_id((*i)->id())) {
3346 RegionFactory::map_add(*i);
3350 in_partition = false;
3355 Playlist::fade_range (list<AudioRange>& ranges)
3357 RegionReadLock rlock (this);
3358 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ) {
3359 list<AudioRange>::iterator tmpr = r;
3361 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ) {
3362 RegionList::const_iterator tmpi = i;
3364 (*i)->fade_range ((*r).start, (*r).end);
3372 Playlist::max_source_level () const
3374 RegionReadLock rlock (const_cast<Playlist *> (this));
3377 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3378 lvl = max (lvl, (*i)->max_source_level());
3385 Playlist::set_orig_track_id (const PBD::ID& id)
3387 if (shared_with(id)) {
3388 // Swap 'shared_id' / origin_track_id
3390 share_with (_orig_track_id);
3392 _orig_track_id = id;
3396 Playlist::share_with (const PBD::ID& id)
3398 if (!shared_with(id)) {
3399 _shared_with_ids.push_back (id);
3404 Playlist::unshare_with (const PBD::ID& id)
3406 list<PBD::ID>::iterator it = _shared_with_ids.begin ();
3407 while (it != _shared_with_ids.end()) {
3409 _shared_with_ids.erase (it);
3417 Playlist::shared_with (const PBD::ID& id) const
3419 bool shared = false;
3420 list<PBD::ID>::const_iterator it = _shared_with_ids.begin ();
3421 while (it != _shared_with_ids.end() && !shared) {
3432 Playlist::reset_shares ()
3434 _shared_with_ids.clear();
3437 /** Take a list of ranges, coalesce any that can be coalesced, then call
3438 * check_crossfades for each one.
3441 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<samplepos_t> > ranges)
3443 /* XXX: it's a shame that this coalesce algorithm also exists in
3444 TimeSelection::consolidate().
3447 /* XXX: xfade: this is implemented in Evoral::RangeList */
3450 for (list<Evoral::Range<samplepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3451 for (list<Evoral::Range<samplepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3457 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3458 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3459 i->from = min (i->from, j->from);
3460 i->to = max (i->to, j->to);
3469 Playlist::set_capture_insertion_in_progress (bool yn)
3471 _capture_insertion_underway = yn;