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/unwind.h"
29 #include "pbd/xml++.h"
31 #include "ardour/debug.h"
32 #include "ardour/midi_region.h"
33 #include "ardour/playlist.h"
34 #include "ardour/playlist_factory.h"
35 #include "ardour/playlist_source.h"
36 #include "ardour/region.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/region_sorters.h"
39 #include "ardour/session.h"
40 #include "ardour/session_playlists.h"
41 #include "ardour/source_factory.h"
42 #include "ardour/tempo.h"
43 #include "ardour/transient_detector.h"
44 #include "ardour/types_convert.h"
49 using namespace ARDOUR;
53 namespace Properties {
54 PBD::PropertyDescriptor<bool> regions;
58 struct ShowMeTheList {
59 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
61 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
63 boost::shared_ptr<Playlist> playlist;
68 Playlist::make_property_quarks ()
70 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
71 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
72 Properties::regions.property_id));
75 RegionListProperty::RegionListProperty (Playlist& pl)
76 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
82 RegionListProperty::RegionListProperty (RegionListProperty const & p)
83 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
84 , _playlist (p._playlist)
90 RegionListProperty::clone () const
92 return new RegionListProperty (*this);
96 RegionListProperty::create () const
98 return new RegionListProperty (_playlist);
102 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
104 /* All regions (even those which are deleted) have their state
105 * saved by other code, so we can just store ID here.
108 node.set_property ("id", region->id());
111 boost::shared_ptr<Region>
112 RegionListProperty::get_content_from_xml (XMLNode const & node) const
115 if (!node.get_property ("id", id)) {
119 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
122 ret = RegionFactory::region_by_id (id);
128 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
129 : SessionObject(sess, nom)
134 first_set_state = false;
139 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
140 : SessionObject(sess, "unnamed playlist")
145 XMLProperty const * prop = node.property("type");
146 assert(!prop || DataType(prop->value()) == _type);
150 _name = "unnamed"; /* reset by set_state */
153 /* set state called by derived class */
156 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
157 : SessionObject(other->_session, namestr)
159 , _type(other->_type)
160 , _orig_track_id (other->_orig_track_id)
161 , _shared_with_ids (other->_shared_with_ids)
166 other->copy_regions (tmp);
170 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
171 add_region_internal( (*x), (*x)->position());
176 _splicing = other->_splicing;
177 _rippling = other->_rippling;
178 _nudging = other->_nudging;
179 _edit_mode = other->_edit_mode;
182 first_set_state = false;
184 in_partition = false;
186 _frozen = other->_frozen;
189 Playlist::Playlist (boost::shared_ptr<const Playlist> other, samplepos_t start, samplecnt_t cnt, string str, bool hide)
190 : SessionObject(other->_session, str)
192 , _type(other->_type)
193 , _orig_track_id (other->_orig_track_id)
194 , _shared_with_ids (other->_shared_with_ids)
196 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
198 samplepos_t end = start + cnt - 1;
204 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
206 boost::shared_ptr<Region> region;
207 boost::shared_ptr<Region> new_region;
208 sampleoffset_t offset = 0;
209 samplepos_t position = 0;
212 Evoral::OverlapType overlap;
216 overlap = region->coverage (start, end);
219 case Evoral::OverlapNone:
222 case Evoral::OverlapInternal:
223 offset = start - region->position();
228 case Evoral::OverlapStart:
230 position = region->position() - start;
231 len = end - region->position();
234 case Evoral::OverlapEnd:
235 offset = start - region->position();
237 len = region->length() - offset;
240 case Evoral::OverlapExternal:
242 position = region->position() - start;
243 len = region->length();
247 RegionFactory::region_name (new_name, region->name(), false);
251 plist.add (Properties::start, region->start() + offset);
252 plist.add (Properties::length, len);
253 plist.add (Properties::name, new_name);
254 plist.add (Properties::layer, region->layer());
255 plist.add (Properties::layering_index, region->layering_index());
257 new_region = RegionFactory::create (region, plist);
259 add_region_internal (new_region, position);
262 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
263 //at the end of construction, any length of cnt beyond the extents of the regions is end_space
264 _end_space = cnt - (get_extent().second - get_extent().first);
267 first_set_state = false;
274 InUse (true); /* EMIT SIGNAL */
285 InUse (false); /* EMIT SIGNAL */
290 Playlist::copy_regions (RegionList& newlist) const
292 RegionReadLock rlock (const_cast<Playlist *> (this));
294 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
295 newlist.push_back (RegionFactory::create (*i, true, true));
300 Playlist::init (bool hide)
302 add_property (regions);
303 _xml_node_name = X_("Playlist");
305 g_atomic_int_set (&block_notifications, 0);
306 g_atomic_int_set (&ignore_state_changes, 0);
307 pending_contents_change = false;
308 pending_layering = false;
309 first_set_state = true;
318 _edit_mode = Config->get_edit_mode();
320 in_partition = false;
323 _capture_insertion_underway = false;
326 _playlist_shift_active = 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 ()
352 /* Playlists are given names like <track name>.<id>
353 * or <track name>.<edit group name>.<id> where id
354 * is an integer. We extract the id and sort by that.
357 size_t dot_position = _name.val().find_last_of(".");
359 if (dot_position == string::npos) {
362 string t = _name.val().substr(dot_position + 1);
364 if (!string_to_uint32 (t, _sort_id)) {
371 Playlist::set_name (const string& str)
373 /* in a typical situation, a playlist is being used
374 * by one diskstream and also is referenced by the
375 * Session. if there are more references than that,
376 * then don't change the name.
383 bool ret = SessionObject::set_name(str);
390 /***********************************************************************
391 * CHANGE NOTIFICATION HANDLING
393 * Notifications must be delayed till the region_lock is released. This
394 * is necessary because handlers for the signals may need to acquire
395 * the lock (e.g. to read from the playlist).
396 ***********************************************************************/
399 Playlist::begin_undo ()
406 Playlist::end_undo ()
415 delay_notifications ();
416 g_atomic_int_inc (&ignore_state_changes);
419 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
421 Playlist::thaw (bool from_undo)
423 g_atomic_int_dec_and_test (&ignore_state_changes);
424 release_notifications (from_undo);
429 Playlist::delay_notifications ()
431 g_atomic_int_inc (&block_notifications);
434 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
436 Playlist::release_notifications (bool from_undo)
438 if (g_atomic_int_dec_and_test (&block_notifications)) {
439 flush_notifications (from_undo);
444 Playlist::notify_contents_changed ()
446 if (holding_state ()) {
447 pending_contents_change = true;
449 pending_contents_change = false;
450 ContentsChanged(); /* EMIT SIGNAL */
455 Playlist::notify_layering_changed ()
457 if (holding_state ()) {
458 pending_layering = true;
460 pending_layering = false;
461 LayeringChanged(); /* EMIT SIGNAL */
466 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
468 if (holding_state ()) {
469 pending_removes.insert (r);
470 pending_contents_change = true;
472 /* this might not be true, but we have to act
473 as though it could be.
475 pending_contents_change = false;
476 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
477 ContentsChanged (); /* EMIT SIGNAL */
482 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
484 Evoral::RangeMove<samplepos_t> const move (r->last_position (), r->length (), r->position ());
486 if (holding_state ()) {
488 pending_range_moves.push_back (move);
492 list< Evoral::RangeMove<samplepos_t> > m;
494 RangesMoved (m, false);
500 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
502 if (r->position() >= r->last_position()) {
503 /* trimmed shorter */
507 Evoral::Range<samplepos_t> const extra (r->position(), r->last_position());
509 if (holding_state ()) {
511 pending_region_extensions.push_back (extra);
515 list<Evoral::Range<samplepos_t> > r;
523 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
525 if (r->length() < r->last_length()) {
526 /* trimmed shorter */
529 Evoral::Range<samplepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
531 if (holding_state ()) {
533 pending_region_extensions.push_back (extra);
537 list<Evoral::Range<samplepos_t> > r;
545 Playlist::notify_region_added (boost::shared_ptr<Region> r)
547 /* the length change might not be true, but we have to act
548 * as though it could be.
551 if (holding_state()) {
552 pending_adds.insert (r);
553 pending_contents_change = true;
556 pending_contents_change = false;
557 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
558 ContentsChanged (); /* EMIT SIGNAL */
563 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
565 Playlist::flush_notifications (bool from_undo)
567 set<boost::shared_ptr<Region> >::iterator s;
568 bool regions_changed = false;
576 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
577 regions_changed = true;
580 /* XXX: it'd be nice if we could use pending_bounds for
581 RegionsExtended and RegionsMoved.
584 /* we have no idea what order the regions ended up in pending
585 * bounds (it could be based on selection order, for example).
586 * so, to preserve layering in the "most recently moved is higher"
587 * model, sort them by existing layer, then timestamp them.
590 // RegionSortByLayer cmp;
591 // pending_bounds.sort (cmp);
593 list<Evoral::Range<samplepos_t> > crossfade_ranges;
595 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
596 crossfade_ranges.push_back ((*r)->last_range ());
597 crossfade_ranges.push_back ((*r)->range ());
600 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
601 crossfade_ranges.push_back ((*s)->range ());
602 remove_dependents (*s);
603 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
606 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
607 crossfade_ranges.push_back ((*s)->range ());
608 /* don't emit RegionAdded signal until relayering is done,
609 so that the region is fully setup by the time
610 anyone hears that its been added
614 /* notify about contents/region changes first so that layering changes
615 * in a UI will take place on the new contents.
618 if (regions_changed || pending_contents_change) {
619 pending_layering = true;
620 ContentsChanged (); /* EMIT SIGNAL */
623 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
624 (*s)->clear_changes ();
625 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
628 if ((regions_changed && !in_set_state) || pending_layering) {
632 coalesce_and_check_crossfades (crossfade_ranges);
634 if (!pending_range_moves.empty ()) {
635 /* We don't need to check crossfades for these as pending_bounds has
638 RangesMoved (pending_range_moves, from_undo || _playlist_shift_active);
641 if (!pending_region_extensions.empty ()) {
642 RegionsExtended (pending_region_extensions);
651 Playlist::clear_pending ()
653 pending_adds.clear ();
654 pending_removes.clear ();
655 pending_bounds.clear ();
656 pending_range_moves.clear ();
657 pending_region_extensions.clear ();
658 pending_contents_change = false;
659 pending_layering = false;
662 /*************************************************************
663 * PLAYLIST OPERATIONS
664 *************************************************************/
666 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
668 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)
670 RegionWriteLock rlock (this);
671 times = fabs (times);
673 int itimes = (int) floor (times);
675 samplepos_t pos = position;
677 if (times == 1 && auto_partition){
679 partition_internal (pos - 1, (pos + region->length()), true, thawlist);
680 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
681 (*i)->resume_property_changes ();
682 _session.add_command (new StatefulDiffCommand (*i));
687 add_region_internal (region, pos, sub_num, quarter_note, for_music);
688 set_layer (region, DBL_MAX);
689 pos += region->length();
693 /* note that itimes can be zero if we being asked to just
694 * insert a single fraction of the region.
697 for (int i = 0; i < itimes; ++i) {
698 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
699 add_region_internal (copy, pos, sub_num);
700 set_layer (copy, DBL_MAX);
701 pos += region->length();
704 samplecnt_t length = 0;
706 if (floor (times) != times) {
707 length = (samplecnt_t) floor (region->length() * (times - floor (times)));
709 RegionFactory::region_name (name, region->name(), false);
714 plist.add (Properties::start, region->start());
715 plist.add (Properties::length, length);
716 plist.add (Properties::name, name);
717 plist.add (Properties::layer, region->layer());
719 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
720 add_region_internal (sub, pos, sub_num);
721 set_layer (sub, DBL_MAX);
725 possibly_splice_unlocked (position, (pos + length) - position, region);
729 Playlist::set_region_ownership ()
731 RegionWriteLock rl (this);
732 RegionList::iterator i;
733 boost::weak_ptr<Playlist> pl (shared_from_this());
735 for (i = regions.begin(); i != regions.end(); ++i) {
736 (*i)->set_playlist (pl);
741 Playlist::add_region_internal (boost::shared_ptr<Region> region, samplepos_t position, int32_t sub_num, double quarter_note, bool for_music)
743 if (region->data_type() != _type) {
747 RegionSortByPosition cmp;
749 if (!first_set_state) {
750 boost::shared_ptr<Playlist> foo (shared_from_this());
751 region->set_playlist (boost::weak_ptr<Playlist>(foo));
754 region->set_position_music (quarter_note);
756 region->set_position (position, sub_num);
759 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
760 all_regions.insert (region);
762 possibly_splice_unlocked (position, region->length(), region);
764 if (!holding_state ()) {
765 /* layers get assigned from XML state, and are not reset during undo/redo */
769 /* we need to notify the existence of new region before checking dependents. Ick. */
771 notify_region_added (region);
773 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
774 region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
780 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, samplepos_t pos)
782 RegionWriteLock rlock (this);
784 bool old_sp = _splicing;
787 remove_region_internal (old);
788 add_region_internal (newr, pos);
789 set_layer (newr, old->layer ());
793 possibly_splice_unlocked (pos, old->length() - newr->length());
797 Playlist::remove_region (boost::shared_ptr<Region> region)
799 RegionWriteLock rlock (this);
800 remove_region_internal (region);
804 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
806 RegionList::iterator i;
810 region->set_playlist (boost::weak_ptr<Playlist>());
813 /* XXX should probably freeze here .... */
815 for (i = regions.begin(); i != regions.end(); ++i) {
818 samplepos_t pos = (*i)->position();
819 samplecnt_t distance = (*i)->length();
823 possibly_splice_unlocked (pos, -distance);
825 if (!holding_state ()) {
827 remove_dependents (region);
830 notify_region_removed (region);
839 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
841 switch (Config->get_region_equivalence()) {
843 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
844 if ((*i)->exact_equivalent (other)) {
845 results.push_back (*i);
850 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
851 if ((*i)->enclosed_equivalent (other)) {
852 results.push_back (*i);
857 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
858 if ((*i)->overlap_equivalent (other)) {
859 results.push_back (*i);
867 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
869 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
871 if ((*i) && (*i)->region_list_equivalent (other)) {
872 results.push_back (*i);
878 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
880 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
882 if ((*i) && (*i)->any_source_equivalent (other)) {
883 results.push_back (*i);
889 Playlist::partition (samplepos_t start, samplepos_t end, bool cut)
893 RegionWriteLock lock(this);
894 partition_internal (start, end, cut, thawlist);
897 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
898 (*i)->resume_property_changes ();
902 /* If a MIDI region is locked to musical-time, Properties::start is ignored
903 * and _start is overwritten using Properties::start_beats in
904 * add_region_internal() -> Region::set_position() -> MidiRegion::set_position_internal()
906 static void maybe_add_start_beats (TempoMap const& tm, PropertyList& plist, boost::shared_ptr<Region> r, samplepos_t start, samplepos_t end)
908 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(r);
912 double delta_beats = tm.quarter_notes_between_samples (start, end);
913 plist.add (Properties::start_beats, mr->start_beats () + delta_beats);
916 /** Go through each region on the playlist and cut them at start and end, removing the section between
917 * start and end if cutting == true. Regions that lie entirely within start and end are always
921 Playlist::partition_internal (samplepos_t start, samplepos_t end, bool cutting, RegionList& thawlist)
923 RegionList new_regions;
927 boost::shared_ptr<Region> region;
928 boost::shared_ptr<Region> current;
930 RegionList::iterator tmp;
931 Evoral::OverlapType overlap;
932 samplepos_t pos1, pos2, pos3, pos4;
936 /* need to work from a copy, because otherwise the regions we add
937 * during the process get operated on as well.
940 RegionList copy = regions.rlist();
942 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
949 if (current->first_sample() >= start && current->last_sample() < end) {
952 remove_region_internal (current);
958 /* coverage will return OverlapStart if the start coincides
959 * with the end point. we do not partition such a region,
960 * so catch this special case.
963 if (current->first_sample() >= end) {
967 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
971 pos1 = current->position();
974 pos4 = current->last_sample();
976 if (overlap == Evoral::OverlapInternal) {
977 /* split: we need 3 new regions, the front, middle and end.
978 * cut: we need 2 regions, the front and end.
982 * ---------------*************************------------
985 * ---------------*****++++++++++++++++====------------
987 * ---------------*****----------------====------------
991 /* "middle" ++++++ */
993 RegionFactory::region_name (new_name, current->name(), false);
997 plist.add (Properties::start, current->start() + (pos2 - pos1));
998 plist.add (Properties::length, pos3 - pos2);
999 plist.add (Properties::name, new_name);
1000 plist.add (Properties::layer, current->layer ());
1001 plist.add (Properties::layering_index, current->layering_index ());
1002 plist.add (Properties::automatic, true);
1003 plist.add (Properties::left_of_split, true);
1004 plist.add (Properties::right_of_split, true);
1005 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos2 - pos1));
1007 /* see note in :_split_region()
1008 * for MusicSample is needed to offset region-gain
1010 region = RegionFactory::create (current, MusicSample (pos2 - pos1, 0), plist);
1011 add_region_internal (region, start);
1012 new_regions.push_back (region);
1017 RegionFactory::region_name (new_name, current->name(), false);
1021 plist.add (Properties::start, current->start() + (pos3 - pos1));
1022 plist.add (Properties::length, pos4 - pos3);
1023 plist.add (Properties::name, new_name);
1024 plist.add (Properties::layer, current->layer ());
1025 plist.add (Properties::layering_index, current->layering_index ());
1026 plist.add (Properties::automatic, true);
1027 plist.add (Properties::right_of_split, true);
1028 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos3 - pos1));
1030 region = RegionFactory::create (current, MusicSample (pos3 - pos1, 0), plist);
1032 add_region_internal (region, end);
1033 new_regions.push_back (region);
1037 current->clear_changes ();
1038 current->suspend_property_changes ();
1039 thawlist.push_back (current);
1040 current->cut_end (pos2 - 1);
1042 } else if (overlap == Evoral::OverlapEnd) {
1046 ---------------*************************------------
1049 ---------------**************+++++++++++------------
1051 ---------------**************-----------------------
1058 RegionFactory::region_name (new_name, current->name(), false);
1062 plist.add (Properties::start, current->start() + (pos2 - pos1));
1063 plist.add (Properties::length, pos4 - pos2);
1064 plist.add (Properties::name, new_name);
1065 plist.add (Properties::layer, current->layer ());
1066 plist.add (Properties::layering_index, current->layering_index ());
1067 plist.add (Properties::automatic, true);
1068 plist.add (Properties::left_of_split, true);
1069 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start() + (pos2 - pos1));
1071 region = RegionFactory::create (current, MusicSample(pos2 - pos1, 0), plist);
1073 add_region_internal (region, start);
1074 new_regions.push_back (region);
1079 current->clear_changes ();
1080 current->suspend_property_changes ();
1081 thawlist.push_back (current);
1082 current->cut_end (pos2 - 1);
1084 } else if (overlap == Evoral::OverlapStart) {
1086 /* split: we need 2 regions: the front and the end.
1087 * cut: just trim current to skip the cut area
1091 * ---------------*************************------------
1095 * ---------------****+++++++++++++++++++++------------
1097 * -------------------*********************------------
1102 RegionFactory::region_name (new_name, current->name(), false);
1106 plist.add (Properties::start, current->start());
1107 plist.add (Properties::length, pos3 - pos1);
1108 plist.add (Properties::name, new_name);
1109 plist.add (Properties::layer, current->layer ());
1110 plist.add (Properties::layering_index, current->layering_index ());
1111 plist.add (Properties::automatic, true);
1112 plist.add (Properties::right_of_split, true);
1113 maybe_add_start_beats (_session.tempo_map(), plist, current, current->start(), current->start());
1115 region = RegionFactory::create (current, plist);
1117 add_region_internal (region, pos1);
1118 new_regions.push_back (region);
1123 current->clear_changes ();
1124 current->suspend_property_changes ();
1125 thawlist.push_back (current);
1126 current->trim_front (pos3);
1127 } else if (overlap == Evoral::OverlapExternal) {
1129 /* split: no split required.
1130 * cut: remove the region.
1134 * ---------------*************************------------
1139 * ---------------*************************------------
1141 * ----------------------------------------------------
1146 remove_region_internal (current);
1149 new_regions.push_back (current);
1153 in_partition = false;
1156 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1157 samplepos_t wanted_length = end-start;
1158 _end_space = wanted_length - _get_extent().second - _get_extent().first;
1161 boost::shared_ptr<Playlist>
1162 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(samplepos_t, samplecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1164 boost::shared_ptr<Playlist> ret;
1165 boost::shared_ptr<Playlist> pl;
1168 if (ranges.empty()) {
1169 return boost::shared_ptr<Playlist>();
1172 start = ranges.front().start;
1174 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1176 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1178 if (i == ranges.begin()) {
1182 /* paste the next section into the nascent playlist,
1183 * offset to reflect the start of the first range we
1187 ret->paste (pl, (*i).start - start, 1.0f, 0);
1194 boost::shared_ptr<Playlist>
1195 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1197 boost::shared_ptr<Playlist> (Playlist::*pmf)(samplepos_t,samplecnt_t,bool) = &Playlist::cut;
1198 return cut_copy (pmf, ranges, result_is_hidden);
1201 boost::shared_ptr<Playlist>
1202 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1204 boost::shared_ptr<Playlist> (Playlist::*pmf)(samplepos_t,samplecnt_t,bool) = &Playlist::copy;
1205 return cut_copy (pmf, ranges, result_is_hidden);
1208 boost::shared_ptr<Playlist>
1209 Playlist::cut (samplepos_t start, samplecnt_t cnt, bool result_is_hidden)
1211 boost::shared_ptr<Playlist> the_copy;
1212 RegionList thawlist;
1215 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1216 string new_name = _name;
1220 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1221 return boost::shared_ptr<Playlist>();
1225 RegionWriteLock rlock (this);
1226 partition_internal (start, start+cnt-1, true, thawlist);
1229 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1230 (*i)->resume_property_changes();
1236 boost::shared_ptr<Playlist>
1237 Playlist::copy (samplepos_t start, samplecnt_t cnt, bool result_is_hidden)
1241 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1242 string new_name = _name;
1246 // 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... )
1248 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1252 Playlist::paste (boost::shared_ptr<Playlist> other, samplepos_t position, float times, const int32_t sub_num)
1254 times = fabs (times);
1257 RegionReadLock rl2 (other.get());
1259 int itimes = (int) floor (times);
1260 samplepos_t pos = position;
1261 samplecnt_t const shift = other->_get_extent().second;
1262 layer_t top = top_layer ();
1265 RegionWriteLock rl1 (this);
1267 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1268 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1270 /* put these new regions on top of all existing ones, but preserve
1271 the ordering they had in the original playlist.
1274 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1275 set_layer (copy_of_region, copy_of_region->layer() + top);
1287 Playlist::duplicate (boost::shared_ptr<Region> region, samplepos_t position, float times)
1289 duplicate(region, position, region->length(), times);
1292 /** @param gap from the beginning of the region to the next beginning */
1294 Playlist::duplicate (boost::shared_ptr<Region> region, samplepos_t position, samplecnt_t gap, float times)
1296 times = fabs (times);
1298 RegionWriteLock rl (this);
1299 int itimes = (int) floor (times);
1302 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1303 add_region_internal (copy, position);
1304 set_layer (copy, DBL_MAX);
1308 if (floor (times) != times) {
1309 samplecnt_t length = (samplecnt_t) floor (region->length() * (times - floor (times)));
1311 RegionFactory::region_name (name, region->name(), false);
1316 plist.add (Properties::start, region->start());
1317 plist.add (Properties::length, length);
1318 plist.add (Properties::name, name);
1320 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1321 add_region_internal (sub, position);
1322 set_layer (sub, DBL_MAX);
1327 /** @param gap from the beginning of the region to the next beginning */
1328 /** @param end the first sample that does _not_ contain a duplicated sample */
1330 Playlist::duplicate_until (boost::shared_ptr<Region> region, samplepos_t position, samplecnt_t gap, samplepos_t end)
1332 RegionWriteLock rl (this);
1334 while (position + region->length() - 1 < end) {
1335 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1336 add_region_internal (copy, position);
1337 set_layer (copy, DBL_MAX);
1341 if (position < end) {
1342 samplecnt_t length = min (region->length(), end - position);
1344 RegionFactory::region_name (name, region->name(), false);
1349 plist.add (Properties::start, region->start());
1350 plist.add (Properties::length, length);
1351 plist.add (Properties::name, name);
1353 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1354 add_region_internal (sub, position);
1355 set_layer (sub, DBL_MAX);
1361 Playlist::duplicate_range (AudioRange& range, float times)
1363 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1364 samplecnt_t offset = range.end - range.start;
1365 paste (pl, range.start + offset, times, 0);
1369 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1371 if (ranges.empty()) {
1375 samplepos_t min_pos = max_samplepos;
1376 samplepos_t max_pos = 0;
1378 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1381 min_pos = min (min_pos, (*i).start);
1382 max_pos = max (max_pos, (*i).end);
1385 samplecnt_t offset = max_pos - min_pos;
1388 int itimes = (int) floor (times);
1390 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1391 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1392 paste (pl, (*i).start + (offset * count), 1.0f, 0);
1399 Playlist::shift (samplepos_t at, sampleoffset_t distance, bool move_intersected, bool ignore_music_glue)
1401 PBD::Unwinder<bool> uw (_playlist_shift_active, true);
1402 RegionWriteLock rlock (this);
1403 RegionList copy (regions.rlist());
1406 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1408 if ((*r)->last_sample() < at) {
1413 if (at > (*r)->first_sample() && at < (*r)->last_sample()) {
1414 /* intersected region */
1415 if (!move_intersected) {
1420 /* do not move regions glued to music time - that
1421 * has to be done separately.
1424 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1425 fixup.push_back (*r);
1429 (*r)->set_position ((*r)->position() + distance);
1432 /* XXX: may not be necessary; Region::post_set should do this, I think */
1433 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1434 (*r)->recompute_position_from_lock_style (0);
1439 Playlist::split (const MusicSample& at)
1441 RegionWriteLock rlock (this);
1442 RegionList copy (regions.rlist());
1444 /* use a copy since this operation can modify the region list */
1446 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1447 _split_region (*r, at);
1452 Playlist::split_region (boost::shared_ptr<Region> region, const MusicSample& playlist_position)
1454 RegionWriteLock rl (this);
1455 _split_region (region, playlist_position);
1459 Playlist::_split_region (boost::shared_ptr<Region> region, const MusicSample& playlist_position)
1461 if (!region->covers (playlist_position.sample)) {
1465 if (region->position() == playlist_position.sample ||
1466 region->last_sample() == playlist_position.sample) {
1470 boost::shared_ptr<Region> left;
1471 boost::shared_ptr<Region> right;
1473 MusicSample before (playlist_position.sample - region->position(), playlist_position.division);
1474 MusicSample after (region->length() - before.sample, 0);
1478 /* split doesn't change anything about length, so don't try to splice */
1480 bool old_sp = _splicing;
1483 RegionFactory::region_name (before_name, region->name(), false);
1488 plist.add (Properties::length, before.sample);
1489 plist.add (Properties::name, before_name);
1490 plist.add (Properties::left_of_split, true);
1491 plist.add (Properties::layering_index, region->layering_index ());
1492 plist.add (Properties::layer, region->layer ());
1494 /* note: we must use the version of ::create with an offset here,
1495 * since it supplies that offset to the Region constructor, which
1496 * is necessary to get audio region gain envelopes right.
1498 left = RegionFactory::create (region, MusicSample (0, 0), plist, true);
1501 RegionFactory::region_name (after_name, region->name(), false);
1506 plist.add (Properties::length, after.sample);
1507 plist.add (Properties::name, after_name);
1508 plist.add (Properties::right_of_split, true);
1509 plist.add (Properties::layering_index, region->layering_index ());
1510 plist.add (Properties::layer, region->layer ());
1512 /* same note as above */
1513 right = RegionFactory::create (region, before, plist, true);
1516 add_region_internal (left, region->position(), 0);
1517 add_region_internal (right, region->position() + before.sample, before.division);
1519 remove_region_internal (region);
1525 Playlist::AddToSoloSelectedList(const Region* r)
1527 _soloSelectedRegions.insert (r);
1532 Playlist::RemoveFromSoloSelectedList(const Region* r)
1534 _soloSelectedRegions.erase (r);
1539 Playlist::SoloSelectedListIncludes(const Region* r)
1541 std::set<const Region*>::iterator i = _soloSelectedRegions.find(r);
1543 return ( i != _soloSelectedRegions.end() );
1547 Playlist::SoloSelectedActive()
1549 return !_soloSelectedRegions.empty();
1554 Playlist::possibly_splice (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1556 if (_splicing || in_set_state) {
1557 /* don't respond to splicing moves or state setting */
1561 if (_edit_mode == Splice) {
1562 splice_locked (at, distance, exclude);
1567 Playlist::possibly_splice_unlocked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1569 if (_splicing || in_set_state) {
1570 /* don't respond to splicing moves or state setting */
1574 if (_edit_mode == Splice) {
1575 splice_unlocked (at, distance, exclude);
1580 Playlist::splice_locked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1583 RegionWriteLock rl (this);
1584 core_splice (at, distance, exclude);
1589 Playlist::splice_unlocked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1591 core_splice (at, distance, exclude);
1595 Playlist::core_splice (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1599 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1601 if (exclude && (*i) == exclude) {
1605 if ((*i)->position() >= at) {
1606 samplepos_t new_pos = (*i)->position() + distance;
1609 } else if (new_pos >= max_samplepos - (*i)->length()) {
1610 new_pos = max_samplepos - (*i)->length();
1613 (*i)->set_position (new_pos);
1619 notify_contents_changed ();
1623 Playlist::ripple_locked (samplepos_t at, samplecnt_t distance, RegionList *exclude)
1626 RegionWriteLock rl (this);
1627 core_ripple (at, distance, exclude);
1632 Playlist::ripple_unlocked (samplepos_t at, samplecnt_t distance, RegionList *exclude)
1634 core_ripple (at, distance, exclude);
1638 Playlist::core_ripple (samplepos_t at, samplecnt_t distance, RegionList *exclude)
1640 if (distance == 0) {
1645 RegionListProperty copy = regions;
1646 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1647 assert (i != copy.end());
1650 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1655 if ((*i)->position() >= at) {
1656 samplepos_t new_pos = (*i)->position() + distance;
1657 samplepos_t limit = max_samplepos - (*i)->length();
1660 } else if (new_pos >= limit ) {
1664 (*i)->set_position (new_pos);
1669 notify_contents_changed ();
1674 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1676 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1680 if (what_changed.contains (Properties::position)) {
1682 /* remove it from the list then add it back in
1683 * the right place again.
1686 RegionSortByPosition cmp;
1688 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1690 if (i == regions.end()) {
1691 /* the region bounds are being modified but its not currently
1692 * in the region list. we will use its bounds correctly when/if
1699 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1702 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1704 sampleoffset_t delta = 0;
1706 if (what_changed.contains (Properties::position)) {
1707 delta = region->position() - region->last_position();
1710 if (what_changed.contains (Properties::length)) {
1711 delta += region->length() - region->last_length();
1715 possibly_splice (region->last_position() + region->last_length(), delta, region);
1718 if (holding_state ()) {
1719 pending_bounds.push_back (region);
1721 notify_contents_changed ();
1723 list<Evoral::Range<samplepos_t> > xf;
1724 xf.push_back (Evoral::Range<samplepos_t> (region->last_range()));
1725 xf.push_back (Evoral::Range<samplepos_t> (region->range()));
1726 coalesce_and_check_crossfades (xf);
1732 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1734 boost::shared_ptr<Region> region (weak_region.lock());
1740 /* this makes a virtual call to the right kind of playlist ... */
1742 region_changed (what_changed, region);
1746 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1748 PropertyChange our_interests;
1749 PropertyChange bounds;
1750 PropertyChange pos_and_length;
1753 if (in_set_state || in_flush) {
1757 our_interests.add (Properties::muted);
1758 our_interests.add (Properties::layer);
1759 our_interests.add (Properties::opaque);
1761 bounds.add (Properties::start);
1762 bounds.add (Properties::position);
1763 bounds.add (Properties::length);
1765 pos_and_length.add (Properties::position);
1766 pos_and_length.add (Properties::length);
1768 if (what_changed.contains (bounds)) {
1769 region_bounds_changed (what_changed, region);
1770 save = !(_splicing || _nudging);
1773 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1774 notify_region_moved (region);
1775 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1776 notify_region_end_trimmed (region);
1777 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1778 notify_region_start_trimmed (region);
1781 /* don't notify about layer changes, since we are the only object that can initiate
1782 * them, and we notify in ::relayer()
1785 if (what_changed.contains (our_interests)) {
1789 mark_session_dirty ();
1795 Playlist::drop_regions ()
1797 RegionWriteLock rl (this);
1799 all_regions.clear ();
1803 Playlist::sync_all_regions_with_regions ()
1805 RegionWriteLock rl (this);
1807 all_regions.clear ();
1809 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1810 all_regions.insert (*i);
1815 Playlist::clear (bool with_signals)
1818 RegionWriteLock rl (this);
1820 region_state_changed_connections.drop_connections ();
1821 region_drop_references_connections.drop_connections ();
1823 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1824 pending_removes.insert (*i);
1829 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1830 remove_dependents (*s);
1836 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1837 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1840 pending_removes.clear ();
1841 pending_contents_change = false;
1847 /* *********************************************************************
1849 **********************************************************************/
1851 boost::shared_ptr<RegionList>
1852 Playlist::region_list()
1854 RegionReadLock rlock (this);
1855 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1860 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1862 RegionReadLock rlock (const_cast<Playlist*>(this));
1864 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1865 (*i)->deep_sources (sources);
1869 boost::shared_ptr<RegionList>
1870 Playlist::regions_at (samplepos_t sample)
1872 RegionReadLock rlock (this);
1873 return find_regions_at (sample);
1877 Playlist::count_regions_at (samplepos_t sample) const
1879 RegionReadLock rlock (const_cast<Playlist*>(this));
1882 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1883 if ((*i)->covers (sample)) {
1891 boost::shared_ptr<Region>
1892 Playlist::top_region_at (samplepos_t sample)
1894 RegionReadLock rlock (this);
1895 boost::shared_ptr<RegionList> rlist = find_regions_at (sample);
1896 boost::shared_ptr<Region> region;
1898 if (rlist->size()) {
1899 RegionSortByLayer cmp;
1901 region = rlist->back();
1907 boost::shared_ptr<Region>
1908 Playlist::top_unmuted_region_at (samplepos_t sample)
1910 RegionReadLock rlock (this);
1911 boost::shared_ptr<RegionList> rlist = find_regions_at (sample);
1913 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1915 RegionList::iterator tmp = i;
1919 if ((*i)->muted()) {
1926 boost::shared_ptr<Region> region;
1928 if (rlist->size()) {
1929 RegionSortByLayer cmp;
1931 region = rlist->back();
1937 boost::shared_ptr<RegionList>
1938 Playlist::find_regions_at (samplepos_t sample)
1940 /* Caller must hold lock */
1942 boost::shared_ptr<RegionList> rlist (new RegionList);
1944 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1945 if ((*i)->covers (sample)) {
1946 rlist->push_back (*i);
1953 boost::shared_ptr<RegionList>
1954 Playlist::regions_with_start_within (Evoral::Range<samplepos_t> range)
1956 RegionReadLock rlock (this);
1957 boost::shared_ptr<RegionList> rlist (new RegionList);
1959 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1960 if ((*i)->first_sample() >= range.from && (*i)->first_sample() <= range.to) {
1961 rlist->push_back (*i);
1968 boost::shared_ptr<RegionList>
1969 Playlist::regions_with_end_within (Evoral::Range<samplepos_t> range)
1971 RegionReadLock rlock (this);
1972 boost::shared_ptr<RegionList> rlist (new RegionList);
1974 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1975 if ((*i)->last_sample() >= range.from && (*i)->last_sample() <= range.to) {
1976 rlist->push_back (*i);
1983 /** @param start Range start.
1984 * @param end Range end.
1985 * @return regions which have some part within this range.
1987 boost::shared_ptr<RegionList>
1988 Playlist::regions_touched (samplepos_t start, samplepos_t end)
1990 RegionReadLock rlock (this);
1991 return regions_touched_locked (start, end);
1994 boost::shared_ptr<RegionList>
1995 Playlist::regions_touched_locked (samplepos_t start, samplepos_t end)
1997 boost::shared_ptr<RegionList> rlist (new RegionList);
1999 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2000 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
2001 rlist->push_back (*i);
2009 Playlist::find_next_transient (samplepos_t from, int dir)
2011 RegionReadLock rlock (this);
2012 AnalysisFeatureList points;
2013 AnalysisFeatureList these_points;
2015 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2017 if ((*i)->last_sample() < from) {
2021 if ((*i)->first_sample() > from) {
2026 (*i)->get_transients (these_points);
2028 /* add first sample, just, err, because */
2030 these_points.push_back ((*i)->first_sample());
2032 points.insert (points.end(), these_points.begin(), these_points.end());
2033 these_points.clear ();
2036 if (points.empty()) {
2040 TransientDetector::cleanup_transients (points, _session.sample_rate(), 3.0);
2041 bool reached = false;
2044 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
2049 if (reached && (*x) > from) {
2054 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2059 if (reached && (*x) < from) {
2068 boost::shared_ptr<Region>
2069 Playlist::find_next_region (samplepos_t sample, RegionPoint point, int dir)
2071 RegionReadLock rlock (this);
2072 boost::shared_ptr<Region> ret;
2073 samplepos_t closest = max_samplepos;
2075 bool end_iter = false;
2077 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2081 sampleoffset_t distance;
2082 boost::shared_ptr<Region> r = (*i);
2083 samplepos_t pos = 0;
2087 pos = r->first_sample ();
2090 pos = r->last_sample ();
2093 pos = r->sync_position ();
2098 case 1: /* forwards */
2101 if ((distance = pos - sample) < closest) {
2110 default: /* backwards */
2113 if ((distance = sample - pos) < closest) {
2129 Playlist::find_next_region_boundary (samplepos_t sample, int dir)
2131 RegionReadLock rlock (this);
2133 samplepos_t closest = max_samplepos;
2134 samplepos_t ret = -1;
2138 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2140 boost::shared_ptr<Region> r = (*i);
2141 sampleoffset_t distance;
2142 const samplepos_t first_sample = r->first_sample();
2143 const samplepos_t last_sample = r->last_sample();
2145 if (first_sample > sample) {
2147 distance = first_sample - sample;
2149 if (distance < closest) {
2155 if (last_sample > sample) {
2157 distance = last_sample - sample;
2159 if (distance < closest) {
2168 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2170 boost::shared_ptr<Region> r = (*i);
2171 sampleoffset_t distance;
2172 const samplepos_t first_sample = r->first_sample();
2173 const samplepos_t last_sample = r->last_sample();
2175 if (last_sample < sample) {
2177 distance = sample - last_sample;
2179 if (distance < closest) {
2185 if (first_sample < sample) {
2187 distance = sample - first_sample;
2189 if (distance < closest) {
2200 /***********************************************************************/
2203 Playlist::mark_session_dirty ()
2205 if (!in_set_state && !holding_state ()) {
2206 _session.set_dirty();
2211 Playlist::rdiff (vector<Command*>& cmds) const
2213 RegionReadLock rlock (const_cast<Playlist *> (this));
2214 Stateful::rdiff (cmds);
2218 Playlist::clear_owned_changes ()
2220 RegionReadLock rlock (this);
2221 Stateful::clear_owned_changes ();
2225 Playlist::update (const RegionListProperty::ChangeRecord& change)
2227 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2228 name(), change.added.size(), change.removed.size()));
2231 /* add the added regions */
2232 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2233 add_region_internal ((*i), (*i)->position());
2235 /* remove the removed regions */
2236 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2244 Playlist::set_state (const XMLNode& node, int version)
2248 XMLNodeConstIterator niter;
2249 XMLPropertyConstIterator piter;
2250 boost::shared_ptr<Region> region;
2252 bool seen_region_nodes = false;
2257 if (node.name() != "Playlist") {
2267 if (node.get_property (X_("name"), name)) {
2272 /* XXX legacy session: fix up later */
2273 node.get_property (X_("orig-diskstream-id"), _orig_track_id);
2275 node.get_property (X_("orig-track-id"), _orig_track_id);
2276 node.get_property (X_("frozen"), _frozen);
2278 node.get_property (X_("combine-ops"), _combine_ops);
2281 if (node.get_property (X_("shared-with-ids"), shared_ids)) {
2282 if (!shared_ids.empty()) {
2283 vector<string> result;
2284 ::split (shared_ids, result, ',');
2285 vector<string>::iterator it = result.begin();
2286 for (; it != result.end(); ++it) {
2287 _shared_with_ids.push_back (PBD::ID(*it));
2294 nlist = node.children();
2296 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2300 if (child->name() == "Region") {
2302 seen_region_nodes = true;
2305 if (!child->get_property ("id", id)) {
2306 error << _("region state node has no ID, ignored") << endmsg;
2310 if ((region = region_by_id (id))) {
2312 region->suspend_property_changes ();
2314 if (region->set_state (*child, version)) {
2315 region->resume_property_changes ();
2319 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2320 region->suspend_property_changes ();
2322 error << _("Playlist: cannot create region from XML") << endmsg;
2327 RegionWriteLock rlock (this);
2328 add_region_internal (region, region->position());
2331 region->resume_property_changes ();
2336 if (seen_region_nodes && regions.empty()) {
2341 notify_contents_changed ();
2344 first_set_state = false;
2350 Playlist::get_state()
2352 return state (true);
2356 Playlist::get_template()
2358 return state (false);
2361 /** @param full_state true to include regions in the returned state, otherwise false.
2364 Playlist::state (bool full_state)
2366 XMLNode *node = new XMLNode (X_("Playlist"));
2368 node->set_property (X_("id"), id());
2369 node->set_property (X_("name"), name());
2370 node->set_property (X_("type"), _type);
2371 node->set_property (X_("orig-track-id"), _orig_track_id);
2374 list<PBD::ID>::const_iterator it = _shared_with_ids.begin();
2375 for (; it != _shared_with_ids.end(); ++it) {
2376 shared_ids += "," + (*it).to_s();
2378 if (!shared_ids.empty()) {
2379 shared_ids.erase(0,1);
2382 node->set_property (X_("shared-with-ids"), shared_ids);
2383 node->set_property (X_("frozen"), _frozen);
2386 RegionReadLock rlock (this);
2388 node->set_property ("combine-ops", _combine_ops);
2390 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2391 node->add_child_nocopy ((*i)->get_state());
2396 node->add_child_copy (*_extra_xml);
2403 Playlist::empty() const
2405 RegionReadLock rlock (const_cast<Playlist *>(this));
2406 return regions.empty();
2410 Playlist::n_regions() const
2412 RegionReadLock rlock (const_cast<Playlist *>(this));
2413 return regions.size();
2416 /** @return true if the all_regions list is empty, ie this playlist
2417 * has never had a region added to it.
2420 Playlist::all_regions_empty() const
2422 RegionReadLock rl (const_cast<Playlist *> (this));
2423 return all_regions.empty();
2426 pair<samplepos_t, samplepos_t>
2427 Playlist::get_extent () const
2429 RegionReadLock rlock (const_cast<Playlist *>(this));
2430 return _get_extent ();
2433 pair<samplepos_t, samplepos_t>
2434 Playlist::get_extent_with_endspace () const
2436 pair<samplepos_t, samplepos_t> l = get_extent();
2437 l.second += _end_space;
2441 pair<samplepos_t, samplepos_t>
2442 Playlist::_get_extent () const
2444 pair<samplepos_t, samplepos_t> ext (max_samplepos, 0);
2446 if (regions.empty()) {
2451 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2452 pair<samplepos_t, samplepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2453 if (e.first < ext.first) {
2454 ext.first = e.first;
2456 if (e.second > ext.second) {
2457 ext.second = e.second;
2465 Playlist::bump_name (string name, Session &session)
2467 string newname = name;
2470 newname = bump_name_once (newname, '.');
2471 } while (session.playlists->by_name (newname)!=NULL);
2478 Playlist::top_layer() const
2480 RegionReadLock rlock (const_cast<Playlist *> (this));
2483 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2484 top = max (top, (*i)->layer());
2490 Playlist::set_edit_mode (EditMode mode)
2495 struct RelayerSort {
2496 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2497 return a->layering_index() < b->layering_index();
2501 /** Set a new layer for a region. This adjusts the layering indices of all
2502 * regions in the playlist to put the specified region in the appropriate
2503 * place. The actual layering will be fixed up when relayer() happens.
2506 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2508 /* Remove the layer we are setting from our region list, and sort it
2509 * using the layer indeces.
2512 RegionList copy = regions.rlist();
2513 copy.remove (region);
2514 copy.sort (RelayerSort ());
2516 /* Put region back in the right place */
2517 RegionList::iterator i = copy.begin();
2518 while (i != copy.end ()) {
2519 if ((*i)->layer() > new_layer) {
2525 copy.insert (i, region);
2527 setup_layering_indices (copy);
2531 Playlist::setup_layering_indices (RegionList const & regions)
2535 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2536 (*k)->set_layering_index (j++);
2540 struct LaterHigherSort {
2541 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2542 return a->position() < b->position();
2546 /** Take the layering indices of each of our regions, compute the layers
2547 * that they should be on, and write the layers back to the regions.
2550 Playlist::relayer ()
2552 /* never compute layers when setting from XML */
2558 /* Build up a new list of regions on each layer, stored in a set of lists
2559 * each of which represent some period of time on some layer. The idea
2560 * is to avoid having to search the entire region list to establish whether
2561 * each region overlaps another */
2563 /* how many pieces to divide this playlist's time up into */
2564 int const divisions = 512;
2566 /* find the start and end positions of the regions on this playlist */
2567 samplepos_t start = INT64_MAX;
2568 samplepos_t end = 0;
2569 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2570 start = min (start, (*i)->position());
2571 end = max (end, (*i)->position() + (*i)->length());
2574 /* hence the size of each time division */
2575 double const division_size = (end - start) / double (divisions);
2577 vector<vector<RegionList> > layers;
2578 layers.push_back (vector<RegionList> (divisions));
2580 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2581 RegionList copy = regions.rlist();
2582 switch (Config->get_layer_model()) {
2584 copy.sort (LaterHigherSort ());
2587 copy.sort (RelayerSort ());
2591 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2592 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2593 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2596 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2598 /* find the time divisions that this region covers; if there are no regions on the list,
2599 * division_size will equal 0 and in this case we'll just say that
2600 * start_division = end_division = 0.
2602 int start_division = 0;
2603 int end_division = 0;
2605 if (division_size > 0) {
2606 start_division = floor ( ((*i)->position() - start) / division_size);
2607 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2608 if (end_division == divisions) {
2613 assert (divisions == 0 || end_division < divisions);
2615 /* find the lowest layer that this region can go on */
2616 size_t j = layers.size();
2618 /* try layer j - 1; it can go on if it overlaps no other region
2619 * that is already on that layer
2622 bool overlap = false;
2623 for (int k = start_division; k <= end_division; ++k) {
2624 RegionList::iterator l = layers[j-1][k].begin ();
2625 while (l != layers[j-1][k].end()) {
2626 if ((*l)->overlap_equivalent (*i)) {
2639 /* overlap, so we must use layer j */
2646 if (j == layers.size()) {
2647 /* we need a new layer for this region */
2648 layers.push_back (vector<RegionList> (divisions));
2651 /* put a reference to this region in each of the divisions that it exists in */
2652 for (int k = start_division; k <= end_division; ++k) {
2653 layers[j][k].push_back (*i);
2656 (*i)->set_layer (j);
2659 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2660 * relayering because we just removed the only region on the top layer, nothing will
2661 * appear to have changed, but the StreamView must still sort itself out. We could
2662 * probably keep a note of the top layer last time we relayered, and check that,
2663 * but premature optimisation &c...
2665 notify_layering_changed ();
2667 /* This relayer() may have been called as a result of a region removal, in which
2668 * case we need to setup layering indices to account for the one that has just
2671 setup_layering_indices (copy);
2675 Playlist::raise_region (boost::shared_ptr<Region> region)
2677 set_layer (region, region->layer() + 1.5);
2682 Playlist::lower_region (boost::shared_ptr<Region> region)
2684 set_layer (region, region->layer() - 1.5);
2689 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2691 set_layer (region, DBL_MAX);
2696 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2698 set_layer (region, -0.5);
2703 Playlist::nudge_after (samplepos_t start, samplecnt_t distance, bool forwards)
2705 RegionList::iterator i;
2711 RegionWriteLock rlock (const_cast<Playlist *> (this));
2713 for (i = regions.begin(); i != regions.end(); ++i) {
2715 if ((*i)->position() >= start) {
2717 samplepos_t new_pos;
2721 if ((*i)->last_sample() > max_samplepos - distance) {
2722 new_pos = max_samplepos - (*i)->length();
2724 new_pos = (*i)->position() + distance;
2729 if ((*i)->position() > distance) {
2730 new_pos = (*i)->position() - distance;
2736 (*i)->set_position (new_pos);
2744 notify_contents_changed ();
2750 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2752 RegionReadLock rlock (const_cast<Playlist*> (this));
2754 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2755 /* Note: passing the second argument as false can cause at best
2756 * incredibly deep and time-consuming recursion, and at worst
2757 * cycles if the user has managed to create cycles of reference
2758 * between compound regions. We generally only this during
2759 * cleanup, and @param shallow is passed as true.
2761 if ((*r)->uses_source (src, shallow)) {
2770 boost::shared_ptr<Region>
2771 Playlist::find_region (const ID& id) const
2773 RegionReadLock rlock (const_cast<Playlist*> (this));
2775 /* searches all regions currently in use by the playlist */
2777 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2778 if ((*i)->id() == id) {
2783 return boost::shared_ptr<Region> ();
2787 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2789 RegionReadLock rlock (const_cast<Playlist*> (this));
2792 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2798 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2799 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2800 /* check if region is used in a compound */
2801 if (it->second == r) {
2802 /* region is referenced as 'original' of a compound */
2806 if (r->whole_file() && r->max_source_level() > 0) {
2807 /* region itself ia a compound.
2808 * the compound regions are not referenced -> check regions inside compound
2810 const SourceList& sl = r->sources();
2811 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2812 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2814 if (ps->playlist()->region_use_count(it->first)) {
2815 // break out of both loops
2824 boost::shared_ptr<Region>
2825 Playlist::region_by_id (const ID& id) const
2827 /* searches all regions ever added to this playlist */
2829 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2830 if ((*i)->id() == id) {
2834 return boost::shared_ptr<Region> ();
2838 Playlist::dump () const
2840 boost::shared_ptr<Region> r;
2842 cerr << "Playlist \"" << _name << "\" " << endl
2843 << regions.size() << " regions "
2846 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2848 cerr << " " << r->name() << " ["
2849 << r->start() << "+" << r->length()
2859 Playlist::set_frozen (bool yn)
2865 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2869 if (region->locked()) {
2876 RegionWriteLock rlock (const_cast<Playlist*> (this));
2881 RegionList::iterator next;
2883 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2884 if ((*i) == region) {
2888 if (next != regions.end()) {
2890 if ((*next)->locked()) {
2894 samplepos_t new_pos;
2896 if ((*next)->position() != region->last_sample() + 1) {
2897 /* they didn't used to touch, so after shuffle,
2898 * just have them swap positions.
2900 new_pos = (*next)->position();
2902 /* they used to touch, so after shuffle,
2903 * make sure they still do. put the earlier
2904 * region where the later one will end after
2907 new_pos = region->position() + (*next)->length();
2910 (*next)->set_position (region->position());
2911 region->set_position (new_pos);
2913 /* avoid a full sort */
2915 regions.erase (i); // removes the region from the list */
2917 regions.insert (next, region); // adds it back after next
2926 RegionList::iterator prev = regions.end();
2928 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2929 if ((*i) == region) {
2931 if (prev != regions.end()) {
2933 if ((*prev)->locked()) {
2937 samplepos_t new_pos;
2938 if (region->position() != (*prev)->last_sample() + 1) {
2939 /* they didn't used to touch, so after shuffle,
2940 * just have them swap positions.
2942 new_pos = region->position();
2944 /* they used to touch, so after shuffle,
2945 * make sure they still do. put the earlier
2946 * one where the later one will end after
2948 new_pos = (*prev)->position() + region->length();
2951 region->set_position ((*prev)->position());
2952 (*prev)->set_position (new_pos);
2954 /* avoid a full sort */
2956 regions.erase (i); // remove region
2957 regions.insert (prev, region); // insert region before prev
2973 notify_contents_changed();
2979 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2981 RegionReadLock rlock (const_cast<Playlist*> (this));
2983 if (regions.size() > 1) {
2991 Playlist::ripple (samplepos_t at, samplecnt_t distance, RegionList *exclude)
2993 ripple_locked (at, distance, exclude);
2997 Playlist::update_after_tempo_map_change ()
2999 RegionWriteLock rlock (const_cast<Playlist*> (this));
3000 RegionList copy (regions.rlist());
3004 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
3005 (*i)->update_after_tempo_map_change ();
3007 /* possibly causes a contents changed notification (flush_notifications()) */
3012 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
3014 RegionReadLock rl (this);
3015 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
3021 Playlist::has_region_at (samplepos_t const p) const
3023 RegionReadLock (const_cast<Playlist *> (this));
3025 RegionList::const_iterator i = regions.begin ();
3026 while (i != regions.end() && !(*i)->covers (p)) {
3030 return (i != regions.end());
3033 /** Look from a session sample time and find the start time of the next region
3034 * which is on the top layer of this playlist.
3035 * @param t Time to look from.
3036 * @return Position of next top-layered region, or max_samplepos if there isn't one.
3039 Playlist::find_next_top_layer_position (samplepos_t t) const
3041 RegionReadLock rlock (const_cast<Playlist *> (this));
3043 layer_t const top = top_layer ();
3045 RegionList copy = regions.rlist ();
3046 copy.sort (RegionSortByPosition ());
3048 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3049 if ((*i)->position() >= t && (*i)->layer() == top) {
3050 return (*i)->position();
3054 return max_samplepos;
3057 boost::shared_ptr<Region>
3058 Playlist::combine (const RegionList& r)
3061 uint32_t channels = 0;
3063 samplepos_t earliest_position = max_samplepos;
3064 vector<TwoRegions> old_and_new_regions;
3065 vector<boost::shared_ptr<Region> > originals;
3066 vector<boost::shared_ptr<Region> > copies;
3069 uint32_t max_level = 0;
3071 /* find the maximum depth of all the regions we're combining */
3073 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3074 max_level = max (max_level, (*i)->max_source_level());
3077 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3078 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3080 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3082 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3083 earliest_position = min (earliest_position, (*i)->position());
3086 /* enable this so that we do not try to create xfades etc. as we add
3090 pl->in_partition = true;
3092 /* sort by position then layer.
3093 * route_time_axis passes 'selected_regions' - which is not sorted.
3094 * here we need the top-most first, then every layer's region sorted by position.
3096 RegionList sorted(r);
3097 sorted.sort(RegionSortByLayerAndPosition());
3099 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3101 /* copy the region */
3103 boost::shared_ptr<Region> original_region = (*i);
3104 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3106 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3107 originals.push_back (original_region);
3108 copies.push_back (copied_region);
3110 RegionFactory::add_compound_association (original_region, copied_region);
3112 /* make position relative to zero */
3114 pl->add_region (copied_region, original_region->position() - earliest_position);
3115 copied_region->set_layer (original_region->layer ());
3117 /* use the maximum number of channels for any region */
3119 channels = max (channels, original_region->n_channels());
3121 /* it will go above the layer of the highest existing region */
3123 layer = max (layer, original_region->layer());
3126 pl->in_partition = false;
3128 pre_combine (copies);
3130 /* now create a new PlaylistSource for each channel in the new playlist */
3133 pair<samplepos_t,samplepos_t> extent = pl->get_extent();
3135 for (uint32_t chn = 0; chn < channels; ++chn) {
3136 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3140 /* now a new whole-file region using the list of sources */
3142 plist.add (Properties::start, 0);
3143 plist.add (Properties::length, extent.second);
3144 plist.add (Properties::name, parent_name);
3145 plist.add (Properties::whole_file, true);
3147 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3149 /* now the non-whole-file region that we will actually use in the playlist */
3152 plist.add (Properties::start, 0);
3153 plist.add (Properties::length, extent.second);
3154 plist.add (Properties::name, child_name);
3155 plist.add (Properties::layer, layer+1);
3157 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3159 /* remove all the selected regions from the current playlist */
3163 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3167 /* do type-specific stuff with the originals and the new compound region */
3169 post_combine (originals, compound_region);
3171 /* add the new region at the right location */
3173 add_region (compound_region, earliest_position);
3179 return compound_region;
3183 Playlist::uncombine (boost::shared_ptr<Region> target)
3185 boost::shared_ptr<PlaylistSource> pls;
3186 boost::shared_ptr<const Playlist> pl;
3187 vector<boost::shared_ptr<Region> > originals;
3188 vector<TwoRegions> old_and_new_regions;
3190 // (1) check that its really a compound region
3192 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3196 pl = pls->playlist();
3198 samplepos_t adjusted_start = 0; // gcc isn't smart enough
3199 samplepos_t adjusted_end = 0; // gcc isn't smart enough
3201 /* the leftmost (earliest) edge of the compound region
3202 * starts at zero in its source, or larger if it
3203 * has been trimmed or content-scrolled.
3205 * the rightmost (latest) edge of the compound region
3206 * relative to its source is the starting point plus
3207 * the length of the region.
3210 // (2) get all the original regions
3212 const RegionList& rl (pl->region_list_property().rlist());
3213 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3214 sampleoffset_t move_offset = 0;
3216 /* there are two possibilities here:
3217 1) the playlist that the playlist source was based on
3218 is us, so just add the originals (which belonged to
3219 us anyway) back in the right place.
3221 2) the playlist that the playlist source was based on
3222 is NOT us, so we need to make copies of each of
3223 the original regions that we find, and add them
3226 bool same_playlist = (pls->original() == id());
3228 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3230 boost::shared_ptr<Region> current (*i);
3232 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3234 if (ca == cassocs.end()) {
3238 boost::shared_ptr<Region> original (ca->second);
3240 bool modified_region;
3242 if (i == rl.begin()) {
3243 move_offset = (target->position() - original->position()) - target->start();
3244 adjusted_start = original->position() + target->start();
3245 adjusted_end = adjusted_start + target->length();
3248 if (!same_playlist) {
3249 samplepos_t pos = original->position();
3250 /* make a copy, but don't announce it */
3251 original = RegionFactory::create (original, false);
3252 /* the pure copy constructor resets position() to zero, so fix that up. */
3253 original->set_position (pos);
3256 /* check to see how the original region (in the
3257 * playlist before compounding occurred) overlaps
3258 * with the new state of the compound region.
3261 original->clear_changes ();
3262 modified_region = false;
3264 switch (original->coverage (adjusted_start, adjusted_end)) {
3265 case Evoral::OverlapNone:
3266 /* original region does not cover any part
3267 * of the current state of the compound region
3271 case Evoral::OverlapInternal:
3272 /* overlap is just a small piece inside the
3273 * original so trim both ends
3275 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3276 modified_region = true;
3279 case Evoral::OverlapExternal:
3280 /* overlap fully covers original, so leave it as is */
3283 case Evoral::OverlapEnd:
3284 /* overlap starts within but covers end, so trim the front of the region */
3285 original->trim_front (adjusted_start);
3286 modified_region = true;
3289 case Evoral::OverlapStart:
3290 /* overlap covers start but ends within, so
3291 * trim the end of the region.
3293 original->trim_end (adjusted_end);
3294 modified_region = true;
3299 /* fix the position to match any movement of the compound region. */
3300 original->set_position (original->position() + move_offset);
3301 modified_region = true;
3304 if (modified_region) {
3305 _session.add_command (new StatefulDiffCommand (original));
3308 /* and add to the list of regions waiting to be
3312 originals.push_back (original);
3313 old_and_new_regions.push_back (TwoRegions (*i, original));
3316 pre_uncombine (originals, target);
3318 in_partition = true;
3321 // (3) remove the compound region
3323 remove_region (target);
3325 // (4) add the constituent regions
3327 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3328 add_region ((*i), (*i)->position());
3329 set_layer((*i), (*i)->layer());
3330 if (!RegionFactory::region_by_id((*i)->id())) {
3331 RegionFactory::map_add(*i);
3335 in_partition = false;
3340 Playlist::fade_range (list<AudioRange>& ranges)
3342 RegionReadLock rlock (this);
3343 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ) {
3344 list<AudioRange>::iterator tmpr = r;
3346 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ) {
3347 RegionList::const_iterator tmpi = i;
3349 (*i)->fade_range ((*r).start, (*r).end);
3357 Playlist::max_source_level () const
3359 RegionReadLock rlock (const_cast<Playlist *> (this));
3362 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3363 lvl = max (lvl, (*i)->max_source_level());
3370 Playlist::set_orig_track_id (const PBD::ID& id)
3372 if (shared_with(id)) {
3373 // Swap 'shared_id' / origin_track_id
3375 share_with (_orig_track_id);
3377 _orig_track_id = id;
3381 Playlist::share_with (const PBD::ID& id)
3383 if (!shared_with(id)) {
3384 _shared_with_ids.push_back (id);
3389 Playlist::unshare_with (const PBD::ID& id)
3391 list<PBD::ID>::iterator it = _shared_with_ids.begin ();
3392 while (it != _shared_with_ids.end()) {
3394 _shared_with_ids.erase (it);
3402 Playlist::shared_with (const PBD::ID& id) const
3404 bool shared = false;
3405 list<PBD::ID>::const_iterator it = _shared_with_ids.begin ();
3406 while (it != _shared_with_ids.end() && !shared) {
3417 Playlist::reset_shares ()
3419 _shared_with_ids.clear();
3422 /** Take a list of ranges, coalesce any that can be coalesced, then call
3423 * check_crossfades for each one.
3426 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<samplepos_t> > ranges)
3428 /* XXX: it's a shame that this coalesce algorithm also exists in
3429 * TimeSelection::consolidate().
3432 /* XXX: xfade: this is implemented in Evoral::RangeList */
3435 for (list<Evoral::Range<samplepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3436 for (list<Evoral::Range<samplepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3442 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3443 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3444 i->from = min (i->from, j->from);
3445 i->to = max (i->to, j->to);
3454 Playlist::set_capture_insertion_in_progress (bool yn)
3456 _capture_insertion_underway = yn;