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 switch (Config->get_region_equivalence()) {
844 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
845 if ((*i)->exact_equivalent (other)) {
846 results.push_back (*i);
851 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
852 if ((*i)->enclosed_equivalent (other)) {
853 results.push_back (*i);
858 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
859 if ((*i)->overlap_equivalent (other)) {
860 results.push_back (*i);
868 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
870 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
872 if ((*i) && (*i)->region_list_equivalent (other)) {
873 results.push_back (*i);
879 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
881 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
883 if ((*i) && (*i)->any_source_equivalent (other)) {
884 results.push_back (*i);
890 Playlist::partition (samplepos_t start, samplepos_t end, bool cut)
894 RegionWriteLock lock(this);
895 partition_internal (start, end, cut, thawlist);
898 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
899 (*i)->resume_property_changes ();
903 /* If a MIDI region is locked to musical-time, Properties::start is ignored
904 * and _start is overwritten using Properties::start_beats in
905 * add_region_internal() -> Region::set_position() -> MidiRegion::set_position_internal()
907 static void maybe_add_start_beats (TempoMap const& tm, PropertyList& plist, boost::shared_ptr<Region> r, samplepos_t start, samplepos_t end)
909 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(r);
913 double delta_beats = tm.quarter_notes_between_samples (start, end);
914 plist.add (Properties::start_beats, mr->start_beats () + delta_beats);
917 /** Go through each region on the playlist and cut them at start and end, removing the section between
918 * start and end if cutting == true. Regions that lie entirely within start and end are always
923 Playlist::partition_internal (samplepos_t start, samplepos_t end, bool cutting, RegionList& thawlist)
925 RegionList new_regions;
929 boost::shared_ptr<Region> region;
930 boost::shared_ptr<Region> current;
932 RegionList::iterator tmp;
933 Evoral::OverlapType overlap;
934 samplepos_t pos1, pos2, pos3, pos4;
938 /* need to work from a copy, because otherwise the regions we add during the process
939 get operated on as well.
942 RegionList copy = regions.rlist();
944 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
951 if (current->first_sample() >= start && current->last_sample() < end) {
954 remove_region_internal (current);
960 /* coverage will return OverlapStart if the start coincides
961 with the end point. we do not partition such a region,
962 so catch this special case.
965 if (current->first_sample() >= end) {
969 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
973 pos1 = current->position();
976 pos4 = current->last_sample();
978 if (overlap == Evoral::OverlapInternal) {
979 /* split: we need 3 new regions, the front, middle and end.
980 cut: we need 2 regions, the front and end.
985 ---------------*************************------------
988 ---------------*****++++++++++++++++====------------
990 ---------------*****----------------====------------
995 /* "middle" ++++++ */
997 RegionFactory::region_name (new_name, current->name(), false);
1001 plist.add (Properties::start, current->start() + (pos2 - pos1));
1002 plist.add (Properties::length, pos3 - pos2);
1003 plist.add (Properties::name, new_name);
1004 plist.add (Properties::layer, current->layer ());
1005 plist.add (Properties::layering_index, current->layering_index ());
1006 plist.add (Properties::automatic, true);
1007 plist.add (Properties::left_of_split, true);
1008 plist.add (Properties::right_of_split, true);
1009 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos2 - pos1));
1011 /* see note in :_split_region()
1012 * for MusicSample is needed to offset region-gain
1014 region = RegionFactory::create (current, MusicSample (pos2 - pos1, 0), plist);
1015 add_region_internal (region, start);
1016 new_regions.push_back (region);
1021 RegionFactory::region_name (new_name, current->name(), false);
1025 plist.add (Properties::start, current->start() + (pos3 - pos1));
1026 plist.add (Properties::length, pos4 - pos3);
1027 plist.add (Properties::name, new_name);
1028 plist.add (Properties::layer, current->layer ());
1029 plist.add (Properties::layering_index, current->layering_index ());
1030 plist.add (Properties::automatic, true);
1031 plist.add (Properties::right_of_split, true);
1032 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos3 - pos1));
1034 region = RegionFactory::create (current, MusicSample (pos3 - pos1, 0), plist);
1036 add_region_internal (region, end);
1037 new_regions.push_back (region);
1041 current->clear_changes ();
1042 current->suspend_property_changes ();
1043 thawlist.push_back (current);
1044 current->cut_end (pos2 - 1);
1046 } else if (overlap == Evoral::OverlapEnd) {
1050 ---------------*************************------------
1053 ---------------**************+++++++++++------------
1055 ---------------**************-----------------------
1062 RegionFactory::region_name (new_name, current->name(), false);
1066 plist.add (Properties::start, current->start() + (pos2 - pos1));
1067 plist.add (Properties::length, pos4 - pos2);
1068 plist.add (Properties::name, new_name);
1069 plist.add (Properties::layer, current->layer ());
1070 plist.add (Properties::layering_index, current->layering_index ());
1071 plist.add (Properties::automatic, true);
1072 plist.add (Properties::left_of_split, true);
1073 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos2 - pos1));
1075 region = RegionFactory::create (current, MusicSample(pos2 - pos1, 0), plist);
1077 add_region_internal (region, start);
1078 new_regions.push_back (region);
1083 current->clear_changes ();
1084 current->suspend_property_changes ();
1085 thawlist.push_back (current);
1086 current->cut_end (pos2 - 1);
1088 } else if (overlap == Evoral::OverlapStart) {
1090 /* split: we need 2 regions: the front and the end.
1091 cut: just trim current to skip the cut area
1096 ---------------*************************------------
1100 ---------------****+++++++++++++++++++++------------
1102 -------------------*********************------------
1108 RegionFactory::region_name (new_name, current->name(), false);
1112 plist.add (Properties::start, current->start());
1113 plist.add (Properties::length, pos3 - pos1);
1114 plist.add (Properties::name, new_name);
1115 plist.add (Properties::layer, current->layer ());
1116 plist.add (Properties::layering_index, current->layering_index ());
1117 plist.add (Properties::automatic, true);
1118 plist.add (Properties::right_of_split, true);
1119 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start());
1121 region = RegionFactory::create (current, plist);
1123 add_region_internal (region, pos1);
1124 new_regions.push_back (region);
1129 current->clear_changes ();
1130 current->suspend_property_changes ();
1131 thawlist.push_back (current);
1132 current->trim_front (pos3);
1133 } else if (overlap == Evoral::OverlapExternal) {
1135 /* split: no split required.
1136 cut: remove the region.
1141 ---------------*************************------------
1145 ---------------*************************------------
1147 ----------------------------------------------------
1152 remove_region_internal (current);
1155 new_regions.push_back (current);
1159 in_partition = false;
1162 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1163 samplepos_t wanted_length = end-start;
1164 _end_space = wanted_length - _get_extent().second - _get_extent().first;
1167 boost::shared_ptr<Playlist>
1168 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(samplepos_t, samplecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1170 boost::shared_ptr<Playlist> ret;
1171 boost::shared_ptr<Playlist> pl;
1174 if (ranges.empty()) {
1175 return boost::shared_ptr<Playlist>();
1178 start = ranges.front().start;
1180 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1182 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1184 if (i == ranges.begin()) {
1188 /* paste the next section into the nascent playlist,
1189 offset to reflect the start of the first range we
1193 ret->paste (pl, (*i).start - start, 1.0f, 0);
1200 boost::shared_ptr<Playlist>
1201 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1203 boost::shared_ptr<Playlist> (Playlist::*pmf)(samplepos_t,samplecnt_t,bool) = &Playlist::cut;
1204 return cut_copy (pmf, ranges, result_is_hidden);
1207 boost::shared_ptr<Playlist>
1208 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1210 boost::shared_ptr<Playlist> (Playlist::*pmf)(samplepos_t,samplecnt_t,bool) = &Playlist::copy;
1211 return cut_copy (pmf, ranges, result_is_hidden);
1214 boost::shared_ptr<Playlist>
1215 Playlist::cut (samplepos_t start, samplecnt_t cnt, bool result_is_hidden)
1217 boost::shared_ptr<Playlist> the_copy;
1218 RegionList thawlist;
1221 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1222 string new_name = _name;
1226 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1227 return boost::shared_ptr<Playlist>();
1231 RegionWriteLock rlock (this);
1232 partition_internal (start, start+cnt-1, true, thawlist);
1235 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1236 (*i)->resume_property_changes();
1242 boost::shared_ptr<Playlist>
1243 Playlist::copy (samplepos_t start, samplecnt_t cnt, bool result_is_hidden)
1247 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1248 string new_name = _name;
1252 // 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... )
1254 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1258 Playlist::paste (boost::shared_ptr<Playlist> other, samplepos_t position, float times, const int32_t sub_num)
1260 times = fabs (times);
1263 RegionReadLock rl2 (other.get());
1265 int itimes = (int) floor (times);
1266 samplepos_t pos = position;
1267 samplecnt_t const shift = other->_get_extent().second;
1268 layer_t top = top_layer ();
1271 RegionWriteLock rl1 (this);
1273 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1274 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1276 /* put these new regions on top of all existing ones, but preserve
1277 the ordering they had in the original playlist.
1280 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1281 set_layer (copy_of_region, copy_of_region->layer() + top);
1293 Playlist::duplicate (boost::shared_ptr<Region> region, samplepos_t position, float times)
1295 duplicate(region, position, region->length(), times);
1298 /** @param gap from the beginning of the region to the next beginning */
1300 Playlist::duplicate (boost::shared_ptr<Region> region, samplepos_t position, samplecnt_t gap, float times)
1302 times = fabs (times);
1304 RegionWriteLock rl (this);
1305 int itimes = (int) floor (times);
1308 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1309 add_region_internal (copy, position);
1310 set_layer (copy, DBL_MAX);
1314 if (floor (times) != times) {
1315 samplecnt_t length = (samplecnt_t) floor (region->length() * (times - floor (times)));
1317 RegionFactory::region_name (name, region->name(), false);
1322 plist.add (Properties::start, region->start());
1323 plist.add (Properties::length, length);
1324 plist.add (Properties::name, name);
1326 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1327 add_region_internal (sub, position);
1328 set_layer (sub, DBL_MAX);
1333 /** @param gap from the beginning of the region to the next beginning */
1334 /** @param end the first sample that does _not_ contain a duplicated sample */
1336 Playlist::duplicate_until (boost::shared_ptr<Region> region, samplepos_t position, samplecnt_t gap, samplepos_t end)
1338 RegionWriteLock rl (this);
1340 while (position + region->length() - 1 < end) {
1341 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1342 add_region_internal (copy, position);
1343 set_layer (copy, DBL_MAX);
1347 if (position < end) {
1348 samplecnt_t length = min (region->length(), end - position);
1350 RegionFactory::region_name (name, region->name(), false);
1355 plist.add (Properties::start, region->start());
1356 plist.add (Properties::length, length);
1357 plist.add (Properties::name, name);
1359 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1360 add_region_internal (sub, position);
1361 set_layer (sub, DBL_MAX);
1367 Playlist::duplicate_range (AudioRange& range, float times)
1369 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1370 samplecnt_t offset = range.end - range.start;
1371 paste (pl, range.start + offset, times, 0);
1375 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1377 if (ranges.empty()) {
1381 samplepos_t min_pos = max_samplepos;
1382 samplepos_t max_pos = 0;
1384 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1387 min_pos = min (min_pos, (*i).start);
1388 max_pos = max (max_pos, (*i).end);
1391 samplecnt_t offset = max_pos - min_pos;
1394 int itimes = (int) floor (times);
1396 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1397 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1398 paste (pl, (*i).start + (offset * count), 1.0f, 0);
1405 Playlist::shift (samplepos_t at, sampleoffset_t distance, bool move_intersected, bool ignore_music_glue)
1407 RegionWriteLock rlock (this);
1408 RegionList copy (regions.rlist());
1411 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1413 if ((*r)->last_sample() < at) {
1418 if (at > (*r)->first_sample() && at < (*r)->last_sample()) {
1419 /* intersected region */
1420 if (!move_intersected) {
1425 /* do not move regions glued to music time - that
1426 has to be done separately.
1429 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1430 fixup.push_back (*r);
1434 (*r)->set_position ((*r)->position() + distance);
1437 /* XXX: may not be necessary; Region::post_set should do this, I think */
1438 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1439 (*r)->recompute_position_from_lock_style (0);
1444 Playlist::split (const MusicSample& at)
1446 RegionWriteLock rlock (this);
1447 RegionList copy (regions.rlist());
1449 /* use a copy since this operation can modify the region list
1452 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1453 _split_region (*r, at);
1458 Playlist::split_region (boost::shared_ptr<Region> region, const MusicSample& playlist_position)
1460 RegionWriteLock rl (this);
1461 _split_region (region, playlist_position);
1465 Playlist::_split_region (boost::shared_ptr<Region> region, const MusicSample& playlist_position)
1467 if (!region->covers (playlist_position.sample)) {
1471 if (region->position() == playlist_position.sample ||
1472 region->last_sample() == playlist_position.sample) {
1476 boost::shared_ptr<Region> left;
1477 boost::shared_ptr<Region> right;
1479 MusicSample before (playlist_position.sample - region->position(), playlist_position.division);
1480 MusicSample after (region->length() - before.sample, 0);
1484 /* split doesn't change anything about length, so don't try to splice */
1486 bool old_sp = _splicing;
1489 RegionFactory::region_name (before_name, region->name(), false);
1494 plist.add (Properties::length, before.sample);
1495 plist.add (Properties::name, before_name);
1496 plist.add (Properties::left_of_split, true);
1497 plist.add (Properties::layering_index, region->layering_index ());
1498 plist.add (Properties::layer, region->layer ());
1500 /* note: we must use the version of ::create with an offset here,
1501 since it supplies that offset to the Region constructor, which
1502 is necessary to get audio region gain envelopes right.
1504 left = RegionFactory::create (region, MusicSample (0, 0), plist, true);
1507 RegionFactory::region_name (after_name, region->name(), false);
1512 plist.add (Properties::length, after.sample);
1513 plist.add (Properties::name, after_name);
1514 plist.add (Properties::right_of_split, true);
1515 plist.add (Properties::layering_index, region->layering_index ());
1516 plist.add (Properties::layer, region->layer ());
1518 /* same note as above */
1519 right = RegionFactory::create (region, before, plist, true);
1522 add_region_internal (left, region->position(), 0);
1523 add_region_internal (right, region->position() + before.sample, before.division);
1525 remove_region_internal (region);
1531 Playlist::AddToSoloSelectedList(const Region* r)
1533 _soloSelectedRegions.insert (r);
1538 Playlist::RemoveFromSoloSelectedList(const Region* r)
1540 _soloSelectedRegions.erase (r);
1545 Playlist::SoloSelectedListIncludes(const Region* r)
1547 std::set<const Region*>::iterator i = _soloSelectedRegions.find(r);
1549 return ( i != _soloSelectedRegions.end() );
1553 Playlist::SoloSelectedActive()
1555 return !_soloSelectedRegions.empty();
1560 Playlist::possibly_splice (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1562 if (_splicing || in_set_state) {
1563 /* don't respond to splicing moves or state setting */
1567 if (_edit_mode == Splice) {
1568 splice_locked (at, distance, exclude);
1573 Playlist::possibly_splice_unlocked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1575 if (_splicing || in_set_state) {
1576 /* don't respond to splicing moves or state setting */
1580 if (_edit_mode == Splice) {
1581 splice_unlocked (at, distance, exclude);
1586 Playlist::splice_locked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1589 RegionWriteLock rl (this);
1590 core_splice (at, distance, exclude);
1595 Playlist::splice_unlocked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1597 core_splice (at, distance, exclude);
1601 Playlist::core_splice (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1605 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1607 if (exclude && (*i) == exclude) {
1611 if ((*i)->position() >= at) {
1612 samplepos_t new_pos = (*i)->position() + distance;
1615 } else if (new_pos >= max_samplepos - (*i)->length()) {
1616 new_pos = max_samplepos - (*i)->length();
1619 (*i)->set_position (new_pos);
1625 notify_contents_changed ();
1629 Playlist::ripple_locked (samplepos_t at, samplecnt_t distance, RegionList *exclude)
1632 RegionWriteLock rl (this);
1633 core_ripple (at, distance, exclude);
1638 Playlist::ripple_unlocked (samplepos_t at, samplecnt_t distance, RegionList *exclude)
1640 core_ripple (at, distance, exclude);
1644 Playlist::core_ripple (samplepos_t at, samplecnt_t distance, RegionList *exclude)
1646 if (distance == 0) {
1651 RegionListProperty copy = regions;
1652 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1653 assert (i != copy.end());
1656 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1661 if ((*i)->position() >= at) {
1662 samplepos_t new_pos = (*i)->position() + distance;
1663 samplepos_t limit = max_samplepos - (*i)->length();
1666 } else if (new_pos >= limit ) {
1670 (*i)->set_position (new_pos);
1675 notify_contents_changed ();
1680 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1682 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1686 if (what_changed.contains (Properties::position)) {
1688 /* remove it from the list then add it back in
1689 the right place again.
1692 RegionSortByPosition cmp;
1694 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1696 if (i == regions.end()) {
1697 /* the region bounds are being modified but its not currently
1698 in the region list. we will use its bounds correctly when/if
1705 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1708 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1710 sampleoffset_t delta = 0;
1712 if (what_changed.contains (Properties::position)) {
1713 delta = region->position() - region->last_position();
1716 if (what_changed.contains (Properties::length)) {
1717 delta += region->length() - region->last_length();
1721 possibly_splice (region->last_position() + region->last_length(), delta, region);
1724 if (holding_state ()) {
1725 pending_bounds.push_back (region);
1727 notify_contents_changed ();
1729 list<Evoral::Range<samplepos_t> > xf;
1730 xf.push_back (Evoral::Range<samplepos_t> (region->last_range()));
1731 xf.push_back (Evoral::Range<samplepos_t> (region->range()));
1732 coalesce_and_check_crossfades (xf);
1738 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1740 boost::shared_ptr<Region> region (weak_region.lock());
1746 /* this makes a virtual call to the right kind of playlist ... */
1748 region_changed (what_changed, region);
1752 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1754 PropertyChange our_interests;
1755 PropertyChange bounds;
1756 PropertyChange pos_and_length;
1759 if (in_set_state || in_flush) {
1763 our_interests.add (Properties::muted);
1764 our_interests.add (Properties::layer);
1765 our_interests.add (Properties::opaque);
1767 bounds.add (Properties::start);
1768 bounds.add (Properties::position);
1769 bounds.add (Properties::length);
1771 pos_and_length.add (Properties::position);
1772 pos_and_length.add (Properties::length);
1774 if (what_changed.contains (bounds)) {
1775 region_bounds_changed (what_changed, region);
1776 save = !(_splicing || _nudging);
1779 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1780 notify_region_moved (region);
1781 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1782 notify_region_end_trimmed (region);
1783 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1784 notify_region_start_trimmed (region);
1787 /* don't notify about layer changes, since we are the only object that can initiate
1788 them, and we notify in ::relayer()
1791 if (what_changed.contains (our_interests)) {
1795 mark_session_dirty ();
1801 Playlist::drop_regions ()
1803 RegionWriteLock rl (this);
1805 all_regions.clear ();
1809 Playlist::sync_all_regions_with_regions ()
1811 RegionWriteLock rl (this);
1813 all_regions.clear ();
1815 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1816 all_regions.insert (*i);
1821 Playlist::clear (bool with_signals)
1824 RegionWriteLock rl (this);
1826 region_state_changed_connections.drop_connections ();
1827 region_drop_references_connections.drop_connections ();
1829 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1830 pending_removes.insert (*i);
1835 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1836 remove_dependents (*s);
1842 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1843 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1846 pending_removes.clear ();
1847 pending_contents_change = false;
1853 /* *********************************************************************
1855 **********************************************************************/
1857 boost::shared_ptr<RegionList>
1858 Playlist::region_list()
1860 RegionReadLock rlock (this);
1861 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1866 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1868 RegionReadLock rlock (const_cast<Playlist*>(this));
1870 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1871 (*i)->deep_sources (sources);
1875 boost::shared_ptr<RegionList>
1876 Playlist::regions_at (samplepos_t sample)
1878 RegionReadLock rlock (this);
1879 return find_regions_at (sample);
1883 Playlist::count_regions_at (samplepos_t sample) const
1885 RegionReadLock rlock (const_cast<Playlist*>(this));
1888 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1889 if ((*i)->covers (sample)) {
1897 boost::shared_ptr<Region>
1898 Playlist::top_region_at (samplepos_t sample)
1901 RegionReadLock rlock (this);
1902 boost::shared_ptr<RegionList> rlist = find_regions_at (sample);
1903 boost::shared_ptr<Region> region;
1905 if (rlist->size()) {
1906 RegionSortByLayer cmp;
1908 region = rlist->back();
1914 boost::shared_ptr<Region>
1915 Playlist::top_unmuted_region_at (samplepos_t sample)
1918 RegionReadLock rlock (this);
1919 boost::shared_ptr<RegionList> rlist = find_regions_at (sample);
1921 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1923 RegionList::iterator tmp = i;
1927 if ((*i)->muted()) {
1934 boost::shared_ptr<Region> region;
1936 if (rlist->size()) {
1937 RegionSortByLayer cmp;
1939 region = rlist->back();
1945 boost::shared_ptr<RegionList>
1946 Playlist::find_regions_at (samplepos_t sample)
1948 /* Caller must hold lock */
1950 boost::shared_ptr<RegionList> rlist (new RegionList);
1952 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1953 if ((*i)->covers (sample)) {
1954 rlist->push_back (*i);
1961 boost::shared_ptr<RegionList>
1962 Playlist::regions_with_start_within (Evoral::Range<samplepos_t> range)
1964 RegionReadLock rlock (this);
1965 boost::shared_ptr<RegionList> rlist (new RegionList);
1967 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1968 if ((*i)->first_sample() >= range.from && (*i)->first_sample() <= range.to) {
1969 rlist->push_back (*i);
1976 boost::shared_ptr<RegionList>
1977 Playlist::regions_with_end_within (Evoral::Range<samplepos_t> range)
1979 RegionReadLock rlock (this);
1980 boost::shared_ptr<RegionList> rlist (new RegionList);
1982 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1983 if ((*i)->last_sample() >= range.from && (*i)->last_sample() <= range.to) {
1984 rlist->push_back (*i);
1991 /** @param start Range start.
1992 * @param end Range end.
1993 * @return regions which have some part within this range.
1995 boost::shared_ptr<RegionList>
1996 Playlist::regions_touched (samplepos_t start, samplepos_t end)
1998 RegionReadLock rlock (this);
1999 return regions_touched_locked (start, end);
2002 boost::shared_ptr<RegionList>
2003 Playlist::regions_touched_locked (samplepos_t start, samplepos_t end)
2005 boost::shared_ptr<RegionList> rlist (new RegionList);
2007 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2008 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
2009 rlist->push_back (*i);
2017 Playlist::find_next_transient (samplepos_t from, int dir)
2019 RegionReadLock rlock (this);
2020 AnalysisFeatureList points;
2021 AnalysisFeatureList these_points;
2023 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2025 if ((*i)->last_sample() < from) {
2029 if ((*i)->first_sample() > from) {
2034 (*i)->get_transients (these_points);
2036 /* add first sample, just, err, because */
2038 these_points.push_back ((*i)->first_sample());
2040 points.insert (points.end(), these_points.begin(), these_points.end());
2041 these_points.clear ();
2044 if (points.empty()) {
2048 TransientDetector::cleanup_transients (points, _session.sample_rate(), 3.0);
2049 bool reached = false;
2052 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
2057 if (reached && (*x) > from) {
2062 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2067 if (reached && (*x) < from) {
2076 boost::shared_ptr<Region>
2077 Playlist::find_next_region (samplepos_t sample, RegionPoint point, int dir)
2079 RegionReadLock rlock (this);
2080 boost::shared_ptr<Region> ret;
2081 samplepos_t closest = max_samplepos;
2083 bool end_iter = false;
2085 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2089 sampleoffset_t distance;
2090 boost::shared_ptr<Region> r = (*i);
2091 samplepos_t pos = 0;
2095 pos = r->first_sample ();
2098 pos = r->last_sample ();
2101 pos = r->sync_position ();
2106 case 1: /* forwards */
2109 if ((distance = pos - sample) < closest) {
2118 default: /* backwards */
2121 if ((distance = sample - pos) < closest) {
2137 Playlist::find_next_region_boundary (samplepos_t sample, int dir)
2139 RegionReadLock rlock (this);
2141 samplepos_t closest = max_samplepos;
2142 samplepos_t ret = -1;
2146 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2148 boost::shared_ptr<Region> r = (*i);
2149 sampleoffset_t distance;
2150 const samplepos_t first_sample = r->first_sample();
2151 const samplepos_t last_sample = r->last_sample();
2153 if (first_sample > sample) {
2155 distance = first_sample - sample;
2157 if (distance < closest) {
2163 if (last_sample > sample) {
2165 distance = last_sample - sample;
2167 if (distance < closest) {
2176 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2178 boost::shared_ptr<Region> r = (*i);
2179 sampleoffset_t distance;
2180 const samplepos_t first_sample = r->first_sample();
2181 const samplepos_t last_sample = r->last_sample();
2183 if (last_sample < sample) {
2185 distance = sample - last_sample;
2187 if (distance < closest) {
2193 if (first_sample < sample) {
2195 distance = sample - first_sample;
2197 if (distance < closest) {
2209 /***********************************************************************/
2215 Playlist::mark_session_dirty ()
2217 if (!in_set_state && !holding_state ()) {
2218 _session.set_dirty();
2223 Playlist::rdiff (vector<Command*>& cmds) const
2225 RegionReadLock rlock (const_cast<Playlist *> (this));
2226 Stateful::rdiff (cmds);
2230 Playlist::clear_owned_changes ()
2232 RegionReadLock rlock (this);
2233 Stateful::clear_owned_changes ();
2237 Playlist::update (const RegionListProperty::ChangeRecord& change)
2239 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2240 name(), change.added.size(), change.removed.size()));
2243 /* add the added regions */
2244 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2245 add_region_internal ((*i), (*i)->position());
2247 /* remove the removed regions */
2248 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2256 Playlist::set_state (const XMLNode& node, int version)
2260 XMLNodeConstIterator niter;
2261 XMLPropertyConstIterator piter;
2262 boost::shared_ptr<Region> region;
2264 bool seen_region_nodes = false;
2269 if (node.name() != "Playlist") {
2279 if (node.get_property (X_("name"), name)) {
2284 /* XXX legacy session: fix up later */
2285 node.get_property (X_("orig-diskstream-id"), _orig_track_id);
2287 node.get_property (X_("orig-track-id"), _orig_track_id);
2288 node.get_property (X_("frozen"), _frozen);
2290 node.get_property (X_("combine-ops"), _combine_ops);
2293 if (node.get_property (X_("shared-with-ids"), shared_ids)) {
2294 if (!shared_ids.empty()) {
2295 vector<string> result;
2296 ::split (shared_ids, result, ',');
2297 vector<string>::iterator it = result.begin();
2298 for (; it != result.end(); ++it) {
2299 _shared_with_ids.push_back (PBD::ID(*it));
2306 nlist = node.children();
2308 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2312 if (child->name() == "Region") {
2314 seen_region_nodes = true;
2317 if (!child->get_property ("id", id)) {
2318 error << _("region state node has no ID, ignored") << endmsg;
2322 if ((region = region_by_id (id))) {
2324 region->suspend_property_changes ();
2326 if (region->set_state (*child, version)) {
2327 region->resume_property_changes ();
2331 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2332 region->suspend_property_changes ();
2334 error << _("Playlist: cannot create region from XML") << endmsg;
2339 RegionWriteLock rlock (this);
2340 add_region_internal (region, region->position());
2343 region->resume_property_changes ();
2348 if (seen_region_nodes && regions.empty()) {
2353 notify_contents_changed ();
2356 first_set_state = false;
2362 Playlist::get_state()
2364 return state (true);
2368 Playlist::get_template()
2370 return state (false);
2373 /** @param full_state true to include regions in the returned state, otherwise false.
2376 Playlist::state (bool full_state)
2378 XMLNode *node = new XMLNode (X_("Playlist"));
2380 node->set_property (X_("id"), id());
2381 node->set_property (X_("name"), name());
2382 node->set_property (X_("type"), _type);
2383 node->set_property (X_("orig-track-id"), _orig_track_id);
2386 list<PBD::ID>::const_iterator it = _shared_with_ids.begin();
2387 for (; it != _shared_with_ids.end(); ++it) {
2388 shared_ids += "," + (*it).to_s();
2390 if (!shared_ids.empty()) {
2391 shared_ids.erase(0,1);
2394 node->set_property (X_("shared-with-ids"), shared_ids);
2395 node->set_property (X_("frozen"), _frozen);
2398 RegionReadLock rlock (this);
2400 node->set_property ("combine-ops", _combine_ops);
2402 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2403 node->add_child_nocopy ((*i)->get_state());
2408 node->add_child_copy (*_extra_xml);
2415 Playlist::empty() const
2417 RegionReadLock rlock (const_cast<Playlist *>(this));
2418 return regions.empty();
2422 Playlist::n_regions() const
2424 RegionReadLock rlock (const_cast<Playlist *>(this));
2425 return regions.size();
2428 /** @return true if the all_regions list is empty, ie this playlist
2429 * has never had a region added to it.
2432 Playlist::all_regions_empty() const
2434 RegionReadLock rl (const_cast<Playlist *> (this));
2435 return all_regions.empty();
2438 pair<samplepos_t, samplepos_t>
2439 Playlist::get_extent () const
2441 RegionReadLock rlock (const_cast<Playlist *>(this));
2442 return _get_extent ();
2445 pair<samplepos_t, samplepos_t>
2446 Playlist::get_extent_with_endspace () const
2448 pair<samplepos_t, samplepos_t> l = get_extent();
2449 l.second += _end_space;
2453 pair<samplepos_t, samplepos_t>
2454 Playlist::_get_extent () const
2456 pair<samplepos_t, samplepos_t> ext (max_samplepos, 0);
2458 if (regions.empty()) {
2463 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2464 pair<samplepos_t, samplepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2465 if (e.first < ext.first) {
2466 ext.first = e.first;
2468 if (e.second > ext.second) {
2469 ext.second = e.second;
2477 Playlist::bump_name (string name, Session &session)
2479 string newname = name;
2482 newname = bump_name_once (newname, '.');
2483 } while (session.playlists->by_name (newname)!=NULL);
2490 Playlist::top_layer() const
2492 RegionReadLock rlock (const_cast<Playlist *> (this));
2495 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2496 top = max (top, (*i)->layer());
2502 Playlist::set_edit_mode (EditMode mode)
2507 struct RelayerSort {
2508 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2509 return a->layering_index() < b->layering_index();
2513 /** Set a new layer for a region. This adjusts the layering indices of all
2514 * regions in the playlist to put the specified region in the appropriate
2515 * place. The actual layering will be fixed up when relayer() happens.
2519 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2521 /* Remove the layer we are setting from our region list, and sort it
2522 * using the layer indeces.
2525 RegionList copy = regions.rlist();
2526 copy.remove (region);
2527 copy.sort (RelayerSort ());
2529 /* Put region back in the right place */
2530 RegionList::iterator i = copy.begin();
2531 while (i != copy.end ()) {
2532 if ((*i)->layer() > new_layer) {
2538 copy.insert (i, region);
2540 setup_layering_indices (copy);
2544 Playlist::setup_layering_indices (RegionList const & regions)
2548 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2549 (*k)->set_layering_index (j++);
2553 struct LaterHigherSort {
2554 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2555 return a->position() < b->position();
2559 /** Take the layering indices of each of our regions, compute the layers
2560 * that they should be on, and write the layers back to the regions.
2563 Playlist::relayer ()
2565 /* never compute layers when setting from XML */
2571 /* Build up a new list of regions on each layer, stored in a set of lists
2572 each of which represent some period of time on some layer. The idea
2573 is to avoid having to search the entire region list to establish whether
2574 each region overlaps another */
2576 /* how many pieces to divide this playlist's time up into */
2577 int const divisions = 512;
2579 /* find the start and end positions of the regions on this playlist */
2580 samplepos_t start = INT64_MAX;
2581 samplepos_t end = 0;
2582 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2583 start = min (start, (*i)->position());
2584 end = max (end, (*i)->position() + (*i)->length());
2587 /* hence the size of each time division */
2588 double const division_size = (end - start) / double (divisions);
2590 vector<vector<RegionList> > layers;
2591 layers.push_back (vector<RegionList> (divisions));
2593 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2594 RegionList copy = regions.rlist();
2595 switch (Config->get_layer_model()) {
2597 copy.sort (LaterHigherSort ());
2600 copy.sort (RelayerSort ());
2604 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2605 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2606 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2609 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2611 /* find the time divisions that this region covers; if there are no regions on the list,
2612 division_size will equal 0 and in this case we'll just say that
2613 start_division = end_division = 0.
2615 int start_division = 0;
2616 int end_division = 0;
2618 if (division_size > 0) {
2619 start_division = floor ( ((*i)->position() - start) / division_size);
2620 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2621 if (end_division == divisions) {
2626 assert (divisions == 0 || end_division < divisions);
2628 /* find the lowest layer that this region can go on */
2629 size_t j = layers.size();
2631 /* try layer j - 1; it can go on if it overlaps no other region
2632 that is already on that layer
2635 bool overlap = false;
2636 for (int k = start_division; k <= end_division; ++k) {
2637 RegionList::iterator l = layers[j-1][k].begin ();
2638 while (l != layers[j-1][k].end()) {
2639 if ((*l)->overlap_equivalent (*i)) {
2652 /* overlap, so we must use layer j */
2659 if (j == layers.size()) {
2660 /* we need a new layer for this region */
2661 layers.push_back (vector<RegionList> (divisions));
2664 /* put a reference to this region in each of the divisions that it exists in */
2665 for (int k = start_division; k <= end_division; ++k) {
2666 layers[j][k].push_back (*i);
2669 (*i)->set_layer (j);
2672 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2673 relayering because we just removed the only region on the top layer, nothing will
2674 appear to have changed, but the StreamView must still sort itself out. We could
2675 probably keep a note of the top layer last time we relayered, and check that,
2676 but premature optimisation &c...
2678 notify_layering_changed ();
2680 /* This relayer() may have been called as a result of a region removal, in which
2681 case we need to setup layering indices to account for the one that has just
2684 setup_layering_indices (copy);
2688 Playlist::raise_region (boost::shared_ptr<Region> region)
2690 set_layer (region, region->layer() + 1.5);
2695 Playlist::lower_region (boost::shared_ptr<Region> region)
2697 set_layer (region, region->layer() - 1.5);
2702 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2704 set_layer (region, DBL_MAX);
2709 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2711 set_layer (region, -0.5);
2716 Playlist::nudge_after (samplepos_t start, samplecnt_t distance, bool forwards)
2718 RegionList::iterator i;
2724 RegionWriteLock rlock (const_cast<Playlist *> (this));
2726 for (i = regions.begin(); i != regions.end(); ++i) {
2728 if ((*i)->position() >= start) {
2730 samplepos_t new_pos;
2734 if ((*i)->last_sample() > max_samplepos - distance) {
2735 new_pos = max_samplepos - (*i)->length();
2737 new_pos = (*i)->position() + distance;
2742 if ((*i)->position() > distance) {
2743 new_pos = (*i)->position() - distance;
2749 (*i)->set_position (new_pos);
2757 notify_contents_changed ();
2763 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2765 RegionReadLock rlock (const_cast<Playlist*> (this));
2767 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2768 /* Note: passing the second argument as false can cause at best
2769 incredibly deep and time-consuming recursion, and at worst
2770 cycles if the user has managed to create cycles of reference
2771 between compound regions. We generally only this during
2772 cleanup, and @param shallow is passed as true.
2774 if ((*r)->uses_source (src, shallow)) {
2783 boost::shared_ptr<Region>
2784 Playlist::find_region (const ID& id) const
2786 RegionReadLock rlock (const_cast<Playlist*> (this));
2788 /* searches all regions currently in use by the playlist */
2790 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2791 if ((*i)->id() == id) {
2796 return boost::shared_ptr<Region> ();
2800 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2802 RegionReadLock rlock (const_cast<Playlist*> (this));
2805 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2811 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2812 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2813 /* check if region is used in a compound */
2814 if (it->second == r) {
2815 /* region is referenced as 'original' of a compound */
2819 if (r->whole_file() && r->max_source_level() > 0) {
2820 /* region itself ia a compound.
2821 * the compound regions are not referenced -> check regions inside compound
2823 const SourceList& sl = r->sources();
2824 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2825 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2827 if (ps->playlist()->region_use_count(it->first)) {
2828 // break out of both loops
2837 boost::shared_ptr<Region>
2838 Playlist::region_by_id (const ID& id) const
2840 /* searches all regions ever added to this playlist */
2842 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2843 if ((*i)->id() == id) {
2847 return boost::shared_ptr<Region> ();
2851 Playlist::dump () const
2853 boost::shared_ptr<Region> r;
2855 cerr << "Playlist \"" << _name << "\" " << endl
2856 << regions.size() << " regions "
2859 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2861 cerr << " " << r->name() << " ["
2862 << r->start() << "+" << r->length()
2872 Playlist::set_frozen (bool yn)
2878 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2882 if (region->locked()) {
2889 RegionWriteLock rlock (const_cast<Playlist*> (this));
2894 RegionList::iterator next;
2896 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2897 if ((*i) == region) {
2901 if (next != regions.end()) {
2903 if ((*next)->locked()) {
2907 samplepos_t new_pos;
2909 if ((*next)->position() != region->last_sample() + 1) {
2910 /* they didn't used to touch, so after shuffle,
2911 just have them swap positions.
2913 new_pos = (*next)->position();
2915 /* they used to touch, so after shuffle,
2916 make sure they still do. put the earlier
2917 region where the later one will end after
2920 new_pos = region->position() + (*next)->length();
2923 (*next)->set_position (region->position());
2924 region->set_position (new_pos);
2926 /* avoid a full sort */
2928 regions.erase (i); // removes the region from the list */
2930 regions.insert (next, region); // adds it back after next
2939 RegionList::iterator prev = regions.end();
2941 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2942 if ((*i) == region) {
2944 if (prev != regions.end()) {
2946 if ((*prev)->locked()) {
2950 samplepos_t new_pos;
2951 if (region->position() != (*prev)->last_sample() + 1) {
2952 /* they didn't used to touch, so after shuffle,
2953 just have them swap positions.
2955 new_pos = region->position();
2957 /* they used to touch, so after shuffle,
2958 make sure they still do. put the earlier
2959 one where the later one will end after
2961 new_pos = (*prev)->position() + region->length();
2964 region->set_position ((*prev)->position());
2965 (*prev)->set_position (new_pos);
2967 /* avoid a full sort */
2969 regions.erase (i); // remove region
2970 regions.insert (prev, region); // insert region before prev
2986 notify_contents_changed();
2992 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2994 RegionReadLock rlock (const_cast<Playlist*> (this));
2996 if (regions.size() > 1) {
3004 Playlist::ripple (samplepos_t at, samplecnt_t distance, RegionList *exclude)
3006 ripple_locked (at, distance, exclude);
3010 Playlist::update_after_tempo_map_change ()
3012 RegionWriteLock rlock (const_cast<Playlist*> (this));
3013 RegionList copy (regions.rlist());
3017 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
3018 (*i)->update_after_tempo_map_change ();
3020 /* possibly causes a contents changed notification (flush_notifications()) */
3025 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
3027 RegionReadLock rl (this);
3028 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
3034 Playlist::has_region_at (samplepos_t const p) const
3036 RegionReadLock (const_cast<Playlist *> (this));
3038 RegionList::const_iterator i = regions.begin ();
3039 while (i != regions.end() && !(*i)->covers (p)) {
3043 return (i != regions.end());
3046 /** Look from a session sample time and find the start time of the next region
3047 * which is on the top layer of this playlist.
3048 * @param t Time to look from.
3049 * @return Position of next top-layered region, or max_samplepos if there isn't one.
3052 Playlist::find_next_top_layer_position (samplepos_t t) const
3054 RegionReadLock rlock (const_cast<Playlist *> (this));
3056 layer_t const top = top_layer ();
3058 RegionList copy = regions.rlist ();
3059 copy.sort (RegionSortByPosition ());
3061 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3062 if ((*i)->position() >= t && (*i)->layer() == top) {
3063 return (*i)->position();
3067 return max_samplepos;
3070 boost::shared_ptr<Region>
3071 Playlist::combine (const RegionList& r)
3074 uint32_t channels = 0;
3076 samplepos_t earliest_position = max_samplepos;
3077 vector<TwoRegions> old_and_new_regions;
3078 vector<boost::shared_ptr<Region> > originals;
3079 vector<boost::shared_ptr<Region> > copies;
3082 uint32_t max_level = 0;
3084 /* find the maximum depth of all the regions we're combining */
3086 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3087 max_level = max (max_level, (*i)->max_source_level());
3090 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3091 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3093 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3095 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3096 earliest_position = min (earliest_position, (*i)->position());
3099 /* enable this so that we do not try to create xfades etc. as we add
3103 pl->in_partition = true;
3105 /* sort by position then layer.
3106 * route_time_axis passes 'selected_regions' - which is not sorted.
3107 * here we need the top-most first, then every layer's region sorted by position.
3109 RegionList sorted(r);
3110 sorted.sort(RegionSortByLayerAndPosition());
3112 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3114 /* copy the region */
3116 boost::shared_ptr<Region> original_region = (*i);
3117 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3119 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3120 originals.push_back (original_region);
3121 copies.push_back (copied_region);
3123 RegionFactory::add_compound_association (original_region, copied_region);
3125 /* make position relative to zero */
3127 pl->add_region (copied_region, original_region->position() - earliest_position);
3128 copied_region->set_layer (original_region->layer ());
3130 /* use the maximum number of channels for any region */
3132 channels = max (channels, original_region->n_channels());
3134 /* it will go above the layer of the highest existing region */
3136 layer = max (layer, original_region->layer());
3139 pl->in_partition = false;
3141 pre_combine (copies);
3143 /* now create a new PlaylistSource for each channel in the new playlist */
3146 pair<samplepos_t,samplepos_t> extent = pl->get_extent();
3148 for (uint32_t chn = 0; chn < channels; ++chn) {
3149 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3153 /* now a new whole-file region using the list of sources */
3155 plist.add (Properties::start, 0);
3156 plist.add (Properties::length, extent.second);
3157 plist.add (Properties::name, parent_name);
3158 plist.add (Properties::whole_file, true);
3160 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3162 /* now the non-whole-file region that we will actually use in the
3167 plist.add (Properties::start, 0);
3168 plist.add (Properties::length, extent.second);
3169 plist.add (Properties::name, child_name);
3170 plist.add (Properties::layer, layer+1);
3172 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3174 /* remove all the selected regions from the current playlist
3179 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3183 /* do type-specific stuff with the originals and the new compound
3187 post_combine (originals, compound_region);
3189 /* add the new region at the right location */
3191 add_region (compound_region, earliest_position);
3197 return compound_region;
3201 Playlist::uncombine (boost::shared_ptr<Region> target)
3203 boost::shared_ptr<PlaylistSource> pls;
3204 boost::shared_ptr<const Playlist> pl;
3205 vector<boost::shared_ptr<Region> > originals;
3206 vector<TwoRegions> old_and_new_regions;
3208 // (1) check that its really a compound region
3210 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3214 pl = pls->playlist();
3216 samplepos_t adjusted_start = 0; // gcc isn't smart enough
3217 samplepos_t adjusted_end = 0; // gcc isn't smart enough
3219 /* the leftmost (earliest) edge of the compound region
3220 starts at zero in its source, or larger if it
3221 has been trimmed or content-scrolled.
3223 the rightmost (latest) edge of the compound region
3224 relative to its source is the starting point plus
3225 the length of the region.
3228 // (2) get all the original regions
3230 const RegionList& rl (pl->region_list_property().rlist());
3231 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3232 sampleoffset_t move_offset = 0;
3234 /* there are two possibilities here:
3235 1) the playlist that the playlist source was based on
3236 is us, so just add the originals (which belonged to
3237 us anyway) back in the right place.
3239 2) the playlist that the playlist source was based on
3240 is NOT us, so we need to make copies of each of
3241 the original regions that we find, and add them
3244 bool same_playlist = (pls->original() == id());
3246 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3248 boost::shared_ptr<Region> current (*i);
3250 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3252 if (ca == cassocs.end()) {
3256 boost::shared_ptr<Region> original (ca->second);
3258 bool modified_region;
3260 if (i == rl.begin()) {
3261 move_offset = (target->position() - original->position()) - target->start();
3262 adjusted_start = original->position() + target->start();
3263 adjusted_end = adjusted_start + target->length();
3266 if (!same_playlist) {
3267 samplepos_t pos = original->position();
3268 /* make a copy, but don't announce it */
3269 original = RegionFactory::create (original, false);
3270 /* the pure copy constructor resets position() to zero,
3273 original->set_position (pos);
3276 /* check to see how the original region (in the
3277 * playlist before compounding occurred) overlaps
3278 * with the new state of the compound region.
3281 original->clear_changes ();
3282 modified_region = false;
3284 switch (original->coverage (adjusted_start, adjusted_end)) {
3285 case Evoral::OverlapNone:
3286 /* original region does not cover any part
3287 of the current state of the compound region
3291 case Evoral::OverlapInternal:
3292 /* overlap is just a small piece inside the
3293 * original so trim both ends
3295 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3296 modified_region = true;
3299 case Evoral::OverlapExternal:
3300 /* overlap fully covers original, so leave it
3305 case Evoral::OverlapEnd:
3306 /* overlap starts within but covers end,
3307 so trim the front of the region
3309 original->trim_front (adjusted_start);
3310 modified_region = true;
3313 case Evoral::OverlapStart:
3314 /* overlap covers start but ends within, so
3315 * trim the end of the region.
3317 original->trim_end (adjusted_end);
3318 modified_region = true;
3323 /* fix the position to match any movement of the compound region.
3325 original->set_position (original->position() + move_offset);
3326 modified_region = true;
3329 if (modified_region) {
3330 _session.add_command (new StatefulDiffCommand (original));
3333 /* and add to the list of regions waiting to be
3337 originals.push_back (original);
3338 old_and_new_regions.push_back (TwoRegions (*i, original));
3341 pre_uncombine (originals, target);
3343 in_partition = true;
3346 // (3) remove the compound region
3348 remove_region (target);
3350 // (4) add the constituent regions
3352 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3353 add_region ((*i), (*i)->position());
3354 set_layer((*i), (*i)->layer());
3355 if (!RegionFactory::region_by_id((*i)->id())) {
3356 RegionFactory::map_add(*i);
3360 in_partition = false;
3365 Playlist::fade_range (list<AudioRange>& ranges)
3367 RegionReadLock rlock (this);
3368 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ) {
3369 list<AudioRange>::iterator tmpr = r;
3371 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ) {
3372 RegionList::const_iterator tmpi = i;
3374 (*i)->fade_range ((*r).start, (*r).end);
3382 Playlist::max_source_level () const
3384 RegionReadLock rlock (const_cast<Playlist *> (this));
3387 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3388 lvl = max (lvl, (*i)->max_source_level());
3395 Playlist::set_orig_track_id (const PBD::ID& id)
3397 if (shared_with(id)) {
3398 // Swap 'shared_id' / origin_track_id
3400 share_with (_orig_track_id);
3402 _orig_track_id = id;
3406 Playlist::share_with (const PBD::ID& id)
3408 if (!shared_with(id)) {
3409 _shared_with_ids.push_back (id);
3414 Playlist::unshare_with (const PBD::ID& id)
3416 list<PBD::ID>::iterator it = _shared_with_ids.begin ();
3417 while (it != _shared_with_ids.end()) {
3419 _shared_with_ids.erase (it);
3427 Playlist::shared_with (const PBD::ID& id) const
3429 bool shared = false;
3430 list<PBD::ID>::const_iterator it = _shared_with_ids.begin ();
3431 while (it != _shared_with_ids.end() && !shared) {
3442 Playlist::reset_shares ()
3444 _shared_with_ids.clear();
3447 /** Take a list of ranges, coalesce any that can be coalesced, then call
3448 * check_crossfades for each one.
3451 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<samplepos_t> > ranges)
3453 /* XXX: it's a shame that this coalesce algorithm also exists in
3454 TimeSelection::consolidate().
3457 /* XXX: xfade: this is implemented in Evoral::RangeList */
3460 for (list<Evoral::Range<samplepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3461 for (list<Evoral::Range<samplepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3467 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3468 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3469 i->from = min (i->from, j->from);
3470 i->to = max (i->to, j->to);
3479 Playlist::set_capture_insertion_in_progress (bool yn)
3481 _capture_insertion_underway = yn;