sane naming scheme for combined regions; fix deadlock when nesting to more than one...
[ardour2.git] / libs / ardour / playlist.cc
blobfd9af56e80c1865e26a02b8a6b0b4960bb7272dc
1 /*
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.
20 #include <stdint.h>
21 #include <set>
22 #include <fstream>
23 #include <algorithm>
24 #include <unistd.h>
25 #include <cerrno>
26 #include <string>
27 #include <climits>
29 #include <boost/lexical_cast.hpp>
31 #include "pbd/failed_constructor.h"
32 #include "pbd/stateful_diff_command.h"
33 #include "pbd/xml++.h"
34 #include "pbd/stacktrace.h"
36 #include "ardour/debug.h"
37 #include "ardour/playlist.h"
38 #include "ardour/session.h"
39 #include "ardour/region.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/playlist_factory.h"
42 #include "ardour/transient_detector.h"
43 #include "ardour/session_playlists.h"
44 #include "ardour/source_factory.h"
46 #include "i18n.h"
48 using namespace std;
49 using namespace ARDOUR;
50 using namespace PBD;
52 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) {}
60 ~ShowMeTheList () {
61 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
63 boost::shared_ptr<Playlist> playlist;
64 string name;
67 struct RegionSortByLayer {
68 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
69 return a->layer() < b->layer();
73 struct RegionSortByLayerWithPending {
74 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
76 double p = a->layer ();
77 if (a->pending_explicit_relayer()) {
78 p += 0.5;
81 double q = b->layer ();
82 if (b->pending_explicit_relayer()) {
83 q += 0.5;
86 return p < q;
90 struct RegionSortByPosition {
91 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
92 return a->position() < b->position();
96 struct RegionSortByLastLayerOp {
97 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
98 return a->last_layer_op() < b->last_layer_op();
102 void
103 Playlist::make_property_quarks ()
105 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
107 Properties::regions.property_id));
110 RegionListProperty::RegionListProperty (Playlist& pl)
111 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
112 , _playlist (pl)
117 RegionListProperty::RegionListProperty (RegionListProperty const & p)
118 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
119 , _playlist (p._playlist)
124 RegionListProperty *
125 RegionListProperty::clone () const
127 return new RegionListProperty (*this);
130 RegionListProperty *
131 RegionListProperty::create () const
133 return new RegionListProperty (_playlist);
136 void
137 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
139 /* All regions (even those which are deleted) have their state saved by other
140 code, so we can just store ID here.
143 node.add_property ("id", region->id().to_s ());
146 boost::shared_ptr<Region>
147 RegionListProperty::get_content_from_xml (XMLNode const & node) const
149 XMLProperty const * prop = node.property ("id");
150 assert (prop);
152 PBD::ID id (prop->value ());
154 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
156 if (!ret) {
157 ret = RegionFactory::region_by_id (id);
160 return ret;
163 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
164 : SessionObject(sess, nom)
165 , regions (*this)
166 , _type(type)
168 init (hide);
169 first_set_state = false;
170 _name = nom;
171 _set_sort_id ();
174 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
175 : SessionObject(sess, "unnamed playlist")
176 , regions (*this)
177 , _type(type)
179 #ifndef NDEBUG
180 const XMLProperty* prop = node.property("type");
181 assert(!prop || DataType(prop->value()) == _type);
182 #endif
184 init (hide);
185 _name = "unnamed"; /* reset by set_state */
186 _set_sort_id ();
188 /* set state called by derived class */
191 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
192 : SessionObject(other->_session, namestr)
193 , regions (*this)
194 , _type(other->_type)
195 , _orig_diskstream_id (other->_orig_diskstream_id)
197 init (hide);
199 RegionList tmp;
200 other->copy_regions (tmp);
202 in_set_state++;
204 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
205 add_region_internal( (*x), (*x)->position());
208 in_set_state--;
210 _splicing = other->_splicing;
211 _nudging = other->_nudging;
212 _edit_mode = other->_edit_mode;
214 in_set_state = 0;
215 first_set_state = false;
216 in_flush = false;
217 in_partition = false;
218 subcnt = 0;
219 _read_data_count = 0;
220 _frozen = other->_frozen;
222 layer_op_counter = other->layer_op_counter;
223 freeze_length = other->freeze_length;
226 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
227 : SessionObject(other->_session, str)
228 , regions (*this)
229 , _type(other->_type)
230 , _orig_diskstream_id (other->_orig_diskstream_id)
232 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
234 framepos_t end = start + cnt - 1;
236 init (hide);
238 in_set_state++;
240 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
242 boost::shared_ptr<Region> region;
243 boost::shared_ptr<Region> new_region;
244 frameoffset_t offset = 0;
245 framepos_t position = 0;
246 framecnt_t len = 0;
247 string new_name;
248 OverlapType overlap;
250 region = *i;
252 overlap = region->coverage (start, end);
254 switch (overlap) {
255 case OverlapNone:
256 continue;
258 case OverlapInternal:
259 offset = start - region->position();
260 position = 0;
261 len = cnt;
262 break;
264 case OverlapStart:
265 offset = 0;
266 position = region->position() - start;
267 len = end - region->position();
268 break;
270 case OverlapEnd:
271 offset = start - region->position();
272 position = 0;
273 len = region->length() - offset;
274 break;
276 case OverlapExternal:
277 offset = 0;
278 position = region->position() - start;
279 len = region->length();
280 break;
283 RegionFactory::region_name (new_name, region->name(), false);
285 PropertyList plist;
287 plist.add (Properties::start, region->start() + offset);
288 plist.add (Properties::length, len);
289 plist.add (Properties::name, new_name);
290 plist.add (Properties::layer, region->layer());
292 new_region = RegionFactory::RegionFactory::create (region, plist);
294 add_region_internal (new_region, position);
297 in_set_state--;
298 first_set_state = false;
300 /* this constructor does NOT notify others (session) */
303 void
304 Playlist::use ()
306 ++_refcnt;
307 InUse (true); /* EMIT SIGNAL */
310 void
311 Playlist::release ()
313 if (_refcnt > 0) {
314 _refcnt--;
317 if (_refcnt == 0) {
318 InUse (false); /* EMIT SIGNAL */
322 void
323 Playlist::copy_regions (RegionList& newlist) const
325 RegionLock rlock (const_cast<Playlist *> (this));
327 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
328 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
332 void
333 Playlist::init (bool hide)
335 add_property (regions);
336 _xml_node_name = X_("Playlist");
338 g_atomic_int_set (&block_notifications, 0);
339 g_atomic_int_set (&ignore_state_changes, 0);
340 pending_contents_change = false;
341 pending_length = false;
342 pending_layering = false;
343 first_set_state = true;
344 _refcnt = 0;
345 _hidden = hide;
346 _splicing = false;
347 _shuffling = false;
348 _nudging = false;
349 in_set_state = 0;
350 in_update = false;
351 _edit_mode = Config->get_edit_mode();
352 in_flush = false;
353 in_partition = false;
354 subcnt = 0;
355 _read_data_count = 0;
356 _frozen = false;
357 layer_op_counter = 0;
358 freeze_length = 0;
359 _explicit_relayering = false;
361 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
362 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
364 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
367 Playlist::~Playlist ()
369 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
372 RegionLock rl (this);
374 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
375 (*i)->set_playlist (boost::shared_ptr<Playlist>());
379 /* GoingAway must be emitted by derived classes */
382 void
383 Playlist::_set_sort_id ()
386 Playlists are given names like <track name>.<id>
387 or <track name>.<edit group name>.<id> where id
388 is an integer. We extract the id and sort by that.
391 size_t dot_position = _name.val().find_last_of(".");
393 if (dot_position == string::npos) {
394 _sort_id = 0;
395 } else {
396 string t = _name.val().substr(dot_position + 1);
398 try {
399 _sort_id = boost::lexical_cast<int>(t);
402 catch (boost::bad_lexical_cast e) {
403 _sort_id = 0;
408 bool
409 Playlist::set_name (const string& str)
411 /* in a typical situation, a playlist is being used
412 by one diskstream and also is referenced by the
413 Session. if there are more references than that,
414 then don't change the name.
417 if (_refcnt > 2) {
418 return false;
421 bool ret = SessionObject::set_name(str);
422 if (ret) {
423 _set_sort_id ();
425 return ret;
428 /***********************************************************************
429 CHANGE NOTIFICATION HANDLING
431 Notifications must be delayed till the region_lock is released. This
432 is necessary because handlers for the signals may need to acquire
433 the lock (e.g. to read from the playlist).
434 ***********************************************************************/
436 void
437 Playlist::begin_undo ()
439 in_update = true;
440 freeze ();
443 void
444 Playlist::end_undo ()
446 thaw (true);
447 in_update = false;
450 void
451 Playlist::freeze ()
453 delay_notifications ();
454 g_atomic_int_inc (&ignore_state_changes);
457 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
458 void
459 Playlist::thaw (bool from_undo)
461 g_atomic_int_dec_and_test (&ignore_state_changes);
462 release_notifications (from_undo);
466 void
467 Playlist::delay_notifications ()
469 g_atomic_int_inc (&block_notifications);
470 freeze_length = _get_extent().second;
473 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
474 void
475 Playlist::release_notifications (bool from_undo)
477 if (g_atomic_int_dec_and_test (&block_notifications)) {
478 flush_notifications (from_undo);
482 void
483 Playlist::notify_contents_changed ()
485 if (holding_state ()) {
486 pending_contents_change = true;
487 } else {
488 pending_contents_change = false;
489 ContentsChanged(); /* EMIT SIGNAL */
493 void
494 Playlist::notify_layering_changed ()
496 if (holding_state ()) {
497 pending_layering = true;
498 } else {
499 pending_layering = false;
500 LayeringChanged(); /* EMIT SIGNAL */
504 void
505 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
507 if (holding_state ()) {
508 pending_removes.insert (r);
509 pending_contents_change = true;
510 pending_length = true;
511 } else {
512 /* this might not be true, but we have to act
513 as though it could be.
515 pending_length = false;
516 LengthChanged (); /* EMIT SIGNAL */
517 pending_contents_change = false;
518 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
519 ContentsChanged (); /* EMIT SIGNAL */
523 void
524 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
526 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
528 if (holding_state ()) {
530 pending_range_moves.push_back (move);
532 } else {
534 list< Evoral::RangeMove<framepos_t> > m;
535 m.push_back (move);
536 RangesMoved (m, false);
541 void
542 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
544 if (r->position() >= r->last_position()) {
545 /* trimmed shorter */
546 return;
549 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
551 if (holding_state ()) {
553 pending_region_extensions.push_back (extra);
555 } else {
557 list<Evoral::Range<framepos_t> > r;
558 r.push_back (extra);
559 RegionsExtended (r);
564 void
565 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
567 if (r->length() < r->last_length()) {
568 /* trimmed shorter */
571 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
573 if (holding_state ()) {
575 pending_region_extensions.push_back (extra);
577 } else {
579 list<Evoral::Range<framepos_t> > r;
580 r.push_back (extra);
581 RegionsExtended (r);
586 void
587 Playlist::notify_region_added (boost::shared_ptr<Region> r)
589 /* the length change might not be true, but we have to act
590 as though it could be.
593 if (holding_state()) {
594 pending_adds.insert (r);
595 pending_contents_change = true;
596 pending_length = true;
597 } else {
598 r->clear_changes ();
599 pending_length = false;
600 LengthChanged (); /* EMIT SIGNAL */
601 pending_contents_change = false;
602 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
603 ContentsChanged (); /* EMIT SIGNAL */
607 void
608 Playlist::notify_length_changed ()
610 if (holding_state ()) {
611 pending_length = true;
612 } else {
613 pending_length = false;
614 LengthChanged(); /* EMIT SIGNAL */
615 pending_contents_change = false;
616 ContentsChanged (); /* EMIT SIGNAL */
620 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
621 void
622 Playlist::flush_notifications (bool from_undo)
624 set<boost::shared_ptr<Region> > dependent_checks_needed;
625 set<boost::shared_ptr<Region> >::iterator s;
626 uint32_t regions_changed = false;
627 bool check_length = false;
628 framecnt_t old_length = 0;
630 if (in_flush) {
631 return;
634 in_flush = true;
636 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
637 regions_changed = true;
638 if (!pending_length) {
639 old_length = _get_extent ().second;
640 check_length = true;
644 /* we have no idea what order the regions ended up in pending
645 bounds (it could be based on selection order, for example).
646 so, to preserve layering in the "most recently moved is higher"
647 model, sort them by existing layer, then timestamp them.
650 // RegionSortByLayer cmp;
651 // pending_bounds.sort (cmp);
653 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
654 if (_session.config.get_layer_model() == MoveAddHigher) {
655 timestamp_layer_op (*r);
657 dependent_checks_needed.insert (*r);
660 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
661 remove_dependents (*s);
662 // cerr << _name << " sends RegionRemoved\n";
663 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
666 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
667 // cerr << _name << " sends RegionAdded\n";
668 /* don't emit RegionAdded signal until relayering is done,
669 so that the region is fully setup by the time
670 anyone hear's that its been added
672 dependent_checks_needed.insert (*s);
675 if (check_length) {
676 if (old_length != _get_extent().second) {
677 pending_length = true;
678 // cerr << _name << " length has changed\n";
682 if (pending_length || (freeze_length != _get_extent().second)) {
683 pending_length = false;
684 // cerr << _name << " sends LengthChanged\n";
685 LengthChanged(); /* EMIT SIGNAL */
688 if (regions_changed || pending_contents_change) {
689 if (!in_set_state) {
690 relayer ();
692 pending_contents_change = false;
693 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
694 ContentsChanged (); /* EMIT SIGNAL */
695 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
698 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
699 (*s)->clear_changes ();
700 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
703 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
704 check_dependents (*s, false);
707 if (!pending_range_moves.empty ()) {
708 RangesMoved (pending_range_moves, from_undo);
711 if (!pending_region_extensions.empty ()) {
712 RegionsExtended (pending_region_extensions);
715 clear_pending ();
717 in_flush = false;
720 void
721 Playlist::clear_pending ()
723 pending_adds.clear ();
724 pending_removes.clear ();
725 pending_bounds.clear ();
726 pending_range_moves.clear ();
727 pending_region_extensions.clear ();
728 pending_contents_change = false;
729 pending_length = false;
732 /*************************************************************
733 PLAYLIST OPERATIONS
734 *************************************************************/
736 void
737 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
739 RegionLock rlock (this);
740 times = fabs (times);
742 int itimes = (int) floor (times);
744 framepos_t pos = position;
746 if (times == 1 && auto_partition){
747 partition(pos - 1, (pos + region->length()), true);
750 if (itimes >= 1) {
751 add_region_internal (region, pos);
752 pos += region->length();
753 --itimes;
757 /* note that itimes can be zero if we being asked to just
758 insert a single fraction of the region.
761 for (int i = 0; i < itimes; ++i) {
762 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
763 add_region_internal (copy, pos);
764 pos += region->length();
767 framecnt_t length = 0;
769 if (floor (times) != times) {
770 length = (framecnt_t) floor (region->length() * (times - floor (times)));
771 string name;
772 RegionFactory::region_name (name, region->name(), false);
775 PropertyList plist;
777 plist.add (Properties::start, region->start());
778 plist.add (Properties::length, length);
779 plist.add (Properties::name, name);
780 plist.add (Properties::layer, region->layer());
782 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
783 add_region_internal (sub, pos);
787 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
790 void
791 Playlist::set_region_ownership ()
793 RegionLock rl (this);
794 RegionList::iterator i;
795 boost::weak_ptr<Playlist> pl (shared_from_this());
797 for (i = regions.begin(); i != regions.end(); ++i) {
798 (*i)->set_playlist (pl);
802 bool
803 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
805 if (region->data_type() != _type){
806 return false;
809 RegionSortByPosition cmp;
811 framecnt_t old_length = 0;
813 if (!holding_state()) {
814 old_length = _get_extent().second;
817 if (!first_set_state) {
818 boost::shared_ptr<Playlist> foo (shared_from_this());
819 region->set_playlist (boost::weak_ptr<Playlist>(foo));
822 region->set_position (position, this);
824 timestamp_layer_op (region);
826 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
827 all_regions.insert (region);
829 possibly_splice_unlocked (position, region->length(), region);
831 if (!holding_state ()) {
832 /* layers get assigned from XML state, and are not reset during undo/redo */
833 relayer ();
836 /* we need to notify the existence of new region before checking dependents. Ick. */
838 notify_region_added (region);
840 if (!holding_state ()) {
842 check_dependents (region, false);
844 if (old_length != _get_extent().second) {
845 notify_length_changed ();
849 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
851 return true;
854 void
855 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
857 RegionLock rlock (this);
859 bool old_sp = _splicing;
860 _splicing = true;
862 remove_region_internal (old);
863 add_region_internal (newr, pos);
865 _splicing = old_sp;
867 possibly_splice_unlocked (pos, old->length() - newr->length());
870 void
871 Playlist::remove_region (boost::shared_ptr<Region> region)
873 RegionLock rlock (this);
874 remove_region_internal (region);
878 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
880 RegionList::iterator i;
881 framecnt_t old_length = 0;
882 int ret = -1;
884 if (!holding_state()) {
885 old_length = _get_extent().second;
888 if (!in_set_state) {
889 /* unset playlist */
890 region->set_playlist (boost::weak_ptr<Playlist>());
893 /* XXX should probably freeze here .... */
895 for (i = regions.begin(); i != regions.end(); ++i) {
896 if (*i == region) {
898 framepos_t pos = (*i)->position();
899 framecnt_t distance = (*i)->length();
901 regions.erase (i);
903 possibly_splice_unlocked (pos, -distance);
905 if (!holding_state ()) {
906 relayer ();
907 remove_dependents (region);
909 if (old_length != _get_extent().second) {
910 notify_length_changed ();
914 notify_region_removed (region);
915 ret = 0;
916 break;
920 return -1;
923 void
924 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
926 if (Config->get_use_overlap_equivalency()) {
927 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
928 if ((*i)->overlap_equivalent (other)) {
929 results.push_back ((*i));
932 } else {
933 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
934 if ((*i)->equivalent (other)) {
935 results.push_back ((*i));
941 void
942 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
944 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
946 if ((*i) && (*i)->region_list_equivalent (other)) {
947 results.push_back (*i);
952 void
953 Playlist::partition (framepos_t start, framepos_t end, bool cut)
955 RegionList thawlist;
957 partition_internal (start, end, cut, thawlist);
959 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
960 (*i)->resume_property_changes ();
964 void
965 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
967 RegionList new_regions;
970 RegionLock rlock (this);
972 boost::shared_ptr<Region> region;
973 boost::shared_ptr<Region> current;
974 string new_name;
975 RegionList::iterator tmp;
976 OverlapType overlap;
977 framepos_t pos1, pos2, pos3, pos4;
979 in_partition = true;
981 /* need to work from a copy, because otherwise the regions we add during the process
982 get operated on as well.
985 RegionList copy = regions.rlist();
987 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
989 tmp = i;
990 ++tmp;
992 current = *i;
994 if (current->first_frame() >= start && current->last_frame() < end) {
996 if (cutting) {
997 remove_region_internal (current);
1000 continue;
1003 /* coverage will return OverlapStart if the start coincides
1004 with the end point. we do not partition such a region,
1005 so catch this special case.
1008 if (current->first_frame() >= end) {
1009 continue;
1012 if ((overlap = current->coverage (start, end)) == OverlapNone) {
1013 continue;
1016 pos1 = current->position();
1017 pos2 = start;
1018 pos3 = end;
1019 pos4 = current->last_frame();
1021 if (overlap == OverlapInternal) {
1022 /* split: we need 3 new regions, the front, middle and end.
1023 cut: we need 2 regions, the front and end.
1027 start end
1028 ---------------*************************------------
1029 P1 P2 P3 P4
1030 SPLIT:
1031 ---------------*****++++++++++++++++====------------
1033 ---------------*****----------------====------------
1037 if (!cutting) {
1038 /* "middle" ++++++ */
1040 RegionFactory::region_name (new_name, current->name(), false);
1042 PropertyList plist;
1044 plist.add (Properties::start, current->start() + (pos2 - pos1));
1045 plist.add (Properties::length, pos3 - pos2);
1046 plist.add (Properties::name, new_name);
1047 plist.add (Properties::layer, regions.size());
1048 plist.add (Properties::automatic, true);
1049 plist.add (Properties::left_of_split, true);
1050 plist.add (Properties::right_of_split, true);
1052 region = RegionFactory::create (current, plist);
1053 add_region_internal (region, start);
1054 new_regions.push_back (region);
1057 /* "end" ====== */
1059 RegionFactory::region_name (new_name, current->name(), false);
1061 PropertyList plist;
1063 plist.add (Properties::start, current->start() + (pos3 - pos1));
1064 plist.add (Properties::length, pos4 - pos3);
1065 plist.add (Properties::name, new_name);
1066 plist.add (Properties::layer, regions.size());
1067 plist.add (Properties::automatic, true);
1068 plist.add (Properties::right_of_split, true);
1070 region = RegionFactory::create (current, plist);
1072 add_region_internal (region, end);
1073 new_regions.push_back (region);
1075 /* "front" ***** */
1077 current->suspend_property_changes ();
1078 thawlist.push_back (current);
1079 current->cut_end (pos2 - 1, this);
1081 } else if (overlap == OverlapEnd) {
1084 start end
1085 ---------------*************************------------
1086 P1 P2 P4 P3
1087 SPLIT:
1088 ---------------**************+++++++++++------------
1089 CUT:
1090 ---------------**************-----------------------
1093 if (!cutting) {
1095 /* end +++++ */
1097 RegionFactory::region_name (new_name, current->name(), false);
1099 PropertyList plist;
1101 plist.add (Properties::start, current->start() + (pos2 - pos1));
1102 plist.add (Properties::length, pos4 - pos2);
1103 plist.add (Properties::name, new_name);
1104 plist.add (Properties::layer, regions.size());
1105 plist.add (Properties::automatic, true);
1106 plist.add (Properties::left_of_split, true);
1108 region = RegionFactory::create (current, plist);
1110 add_region_internal (region, start);
1111 new_regions.push_back (region);
1114 /* front ****** */
1116 current->suspend_property_changes ();
1117 thawlist.push_back (current);
1118 current->cut_end (pos2 - 1, this);
1120 } else if (overlap == OverlapStart) {
1122 /* split: we need 2 regions: the front and the end.
1123 cut: just trim current to skip the cut area
1127 start end
1128 ---------------*************************------------
1129 P2 P1 P3 P4
1131 SPLIT:
1132 ---------------****+++++++++++++++++++++------------
1133 CUT:
1134 -------------------*********************------------
1138 if (!cutting) {
1139 /* front **** */
1140 RegionFactory::region_name (new_name, current->name(), false);
1142 PropertyList plist;
1144 plist.add (Properties::start, current->start());
1145 plist.add (Properties::length, pos3 - pos1);
1146 plist.add (Properties::name, new_name);
1147 plist.add (Properties::layer, regions.size());
1148 plist.add (Properties::automatic, true);
1149 plist.add (Properties::right_of_split, true);
1151 region = RegionFactory::create (current, plist);
1153 add_region_internal (region, pos1);
1154 new_regions.push_back (region);
1157 /* end */
1159 current->suspend_property_changes ();
1160 thawlist.push_back (current);
1161 current->trim_front (pos3, this);
1162 } else if (overlap == OverlapExternal) {
1164 /* split: no split required.
1165 cut: remove the region.
1169 start end
1170 ---------------*************************------------
1171 P2 P1 P3 P4
1173 SPLIT:
1174 ---------------*************************------------
1175 CUT:
1176 ----------------------------------------------------
1180 if (cutting) {
1181 remove_region_internal (current);
1184 new_regions.push_back (current);
1188 in_partition = false;
1191 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1192 check_dependents (*i, false);
1196 boost::shared_ptr<Playlist>
1197 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1199 boost::shared_ptr<Playlist> ret;
1200 boost::shared_ptr<Playlist> pl;
1201 framepos_t start;
1203 if (ranges.empty()) {
1204 return boost::shared_ptr<Playlist>();
1207 start = ranges.front().start;
1209 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1211 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1213 if (i == ranges.begin()) {
1214 ret = pl;
1215 } else {
1217 /* paste the next section into the nascent playlist,
1218 offset to reflect the start of the first range we
1219 chopped.
1222 ret->paste (pl, (*i).start - start, 1.0f);
1226 return ret;
1229 boost::shared_ptr<Playlist>
1230 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1232 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1233 return cut_copy (pmf, ranges, result_is_hidden);
1236 boost::shared_ptr<Playlist>
1237 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1239 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1240 return cut_copy (pmf, ranges, result_is_hidden);
1243 boost::shared_ptr<Playlist>
1244 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1246 boost::shared_ptr<Playlist> the_copy;
1247 RegionList thawlist;
1248 char buf[32];
1250 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1251 string new_name = _name;
1252 new_name += '.';
1253 new_name += buf;
1255 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1256 return boost::shared_ptr<Playlist>();
1259 partition_internal (start, start+cnt-1, true, thawlist);
1261 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1262 (*i)->resume_property_changes();
1265 return the_copy;
1268 boost::shared_ptr<Playlist>
1269 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1271 char buf[32];
1273 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1274 string new_name = _name;
1275 new_name += '.';
1276 new_name += buf;
1278 cnt = min (_get_extent().second - start, cnt);
1279 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1283 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1285 times = fabs (times);
1288 RegionLock rl1 (this);
1289 RegionLock rl2 (other.get());
1291 framecnt_t const old_length = _get_extent().second;
1293 int itimes = (int) floor (times);
1294 framepos_t pos = position;
1295 framecnt_t const shift = other->_get_extent().second;
1296 layer_t top_layer = regions.size();
1298 while (itimes--) {
1299 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1300 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1302 /* put these new regions on top of all existing ones, but preserve
1303 the ordering they had in the original playlist.
1306 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1307 add_region_internal (copy_of_region, (*i)->position() + pos);
1309 pos += shift;
1313 /* XXX shall we handle fractional cases at some point? */
1315 if (old_length != _get_extent().second) {
1316 notify_length_changed ();
1322 return 0;
1326 void
1327 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1329 times = fabs (times);
1331 RegionLock rl (this);
1332 int itimes = (int) floor (times);
1333 framepos_t pos = position + 1;
1335 while (itimes--) {
1336 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1337 add_region_internal (copy, pos);
1338 pos += region->length();
1341 if (floor (times) != times) {
1342 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1343 string name;
1344 RegionFactory::region_name (name, region->name(), false);
1347 PropertyList plist;
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, pos);
1359 void
1360 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1362 RegionLock rlock (this);
1363 RegionList copy (regions.rlist());
1364 RegionList fixup;
1366 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1368 if ((*r)->last_frame() < at) {
1369 /* too early */
1370 continue;
1373 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1374 /* intersected region */
1375 if (!move_intersected) {
1376 continue;
1380 /* do not move regions glued to music time - that
1381 has to be done separately.
1384 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1385 fixup.push_back (*r);
1386 continue;
1389 (*r)->set_position ((*r)->position() + distance, this);
1392 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1393 (*r)->recompute_position_from_lock_style ();
1397 void
1398 Playlist::split (framepos_t at)
1400 RegionLock rlock (this);
1401 RegionList copy (regions.rlist());
1403 /* use a copy since this operation can modify the region list
1406 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1407 _split_region (*r, at);
1411 void
1412 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1414 RegionLock rl (this);
1415 _split_region (region, playlist_position);
1418 void
1419 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1421 if (!region->covers (playlist_position)) {
1422 return;
1425 if (region->position() == playlist_position ||
1426 region->last_frame() == playlist_position) {
1427 return;
1430 boost::shared_ptr<Region> left;
1431 boost::shared_ptr<Region> right;
1432 frameoffset_t before;
1433 frameoffset_t after;
1434 string before_name;
1435 string after_name;
1437 /* split doesn't change anything about length, so don't try to splice */
1439 bool old_sp = _splicing;
1440 _splicing = true;
1442 before = playlist_position - region->position();
1443 after = region->length() - before;
1445 RegionFactory::region_name (before_name, region->name(), false);
1448 PropertyList plist;
1450 plist.add (Properties::position, region->position ());
1451 plist.add (Properties::length, before);
1452 plist.add (Properties::name, before_name);
1453 plist.add (Properties::left_of_split, true);
1455 /* note: we must use the version of ::create with an offset here,
1456 since it supplies that offset to the Region constructor, which
1457 is necessary to get audio region gain envelopes right.
1459 left = RegionFactory::create (region, 0, plist);
1462 RegionFactory::region_name (after_name, region->name(), false);
1465 PropertyList plist;
1467 plist.add (Properties::position, region->position() + before);
1468 plist.add (Properties::length, after);
1469 plist.add (Properties::name, after_name);
1470 plist.add (Properties::right_of_split, true);
1472 /* same note as above */
1473 right = RegionFactory::create (region, before, plist);
1476 add_region_internal (left, region->position());
1477 add_region_internal (right, region->position() + before);
1479 uint64_t orig_layer_op = region->last_layer_op();
1480 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1481 if ((*i)->last_layer_op() > orig_layer_op) {
1482 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1486 left->set_last_layer_op ( orig_layer_op );
1487 right->set_last_layer_op ( orig_layer_op + 1);
1489 layer_op_counter++;
1491 finalize_split_region (region, left, right);
1493 remove_region_internal (region);
1495 _splicing = old_sp;
1498 void
1499 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1501 if (_splicing || in_set_state) {
1502 /* don't respond to splicing moves or state setting */
1503 return;
1506 if (_edit_mode == Splice) {
1507 splice_locked (at, distance, exclude);
1511 void
1512 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1514 if (_splicing || in_set_state) {
1515 /* don't respond to splicing moves or state setting */
1516 return;
1519 if (_edit_mode == Splice) {
1520 splice_unlocked (at, distance, exclude);
1524 void
1525 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1528 RegionLock rl (this);
1529 core_splice (at, distance, exclude);
1533 void
1534 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1536 core_splice (at, distance, exclude);
1539 void
1540 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1542 _splicing = true;
1544 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1546 if (exclude && (*i) == exclude) {
1547 continue;
1550 if ((*i)->position() >= at) {
1551 framepos_t new_pos = (*i)->position() + distance;
1552 if (new_pos < 0) {
1553 new_pos = 0;
1554 } else if (new_pos >= max_framepos - (*i)->length()) {
1555 new_pos = max_framepos - (*i)->length();
1558 (*i)->set_position (new_pos, this);
1562 _splicing = false;
1564 notify_length_changed ();
1567 void
1568 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1570 if (in_set_state || _splicing || _nudging || _shuffling) {
1571 return;
1574 if (what_changed.contains (Properties::position)) {
1576 /* remove it from the list then add it back in
1577 the right place again.
1580 RegionSortByPosition cmp;
1582 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1584 if (i == regions.end()) {
1585 /* the region bounds are being modified but its not currently
1586 in the region list. we will use its bounds correctly when/if
1587 it is added
1589 return;
1592 regions.erase (i);
1593 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1596 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1598 frameoffset_t delta = 0;
1600 if (what_changed.contains (Properties::position)) {
1601 delta = region->position() - region->last_position();
1604 if (what_changed.contains (Properties::length)) {
1605 delta += region->length() - region->last_length();
1608 if (delta) {
1609 possibly_splice (region->last_position() + region->last_length(), delta, region);
1612 if (holding_state ()) {
1613 pending_bounds.push_back (region);
1614 } else {
1615 if (_session.config.get_layer_model() == MoveAddHigher) {
1616 /* it moved or changed length, so change the timestamp */
1617 timestamp_layer_op (region);
1620 notify_length_changed ();
1621 relayer ();
1622 check_dependents (region, false);
1627 void
1628 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1630 boost::shared_ptr<Region> region (weak_region.lock());
1632 if (!region) {
1633 return;
1636 /* this makes a virtual call to the right kind of playlist ... */
1638 region_changed (what_changed, region);
1641 bool
1642 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1644 PropertyChange our_interests;
1645 PropertyChange bounds;
1646 PropertyChange pos_and_length;
1647 bool save = false;
1649 if (in_set_state || in_flush) {
1650 return false;
1653 our_interests.add (Properties::muted);
1654 our_interests.add (Properties::layer);
1655 our_interests.add (Properties::opaque);
1657 bounds.add (Properties::start);
1658 bounds.add (Properties::position);
1659 bounds.add (Properties::length);
1661 pos_and_length.add (Properties::position);
1662 pos_and_length.add (Properties::length);
1664 if (what_changed.contains (bounds)) {
1665 region_bounds_changed (what_changed, region);
1666 save = !(_splicing || _nudging);
1669 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1670 check_dependents (region, false);
1673 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1674 notify_region_moved (region);
1675 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1676 notify_region_end_trimmed (region);
1677 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1678 notify_region_start_trimmed (region);
1681 /* don't notify about layer changes, since we are the only object that can initiate
1682 them, and we notify in ::relayer()
1685 if (what_changed.contains (our_interests)) {
1686 save = true;
1689 return save;
1692 void
1693 Playlist::drop_regions ()
1695 RegionLock rl (this);
1696 regions.clear ();
1697 all_regions.clear ();
1700 void
1701 Playlist::sync_all_regions_with_regions ()
1703 RegionLock rl (this);
1705 all_regions.clear ();
1707 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1708 all_regions.insert (*i);
1712 void
1713 Playlist::clear (bool with_signals)
1716 RegionLock rl (this);
1718 region_state_changed_connections.drop_connections ();
1720 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1721 pending_removes.insert (*i);
1724 regions.clear ();
1726 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1727 remove_dependents (*s);
1731 if (with_signals) {
1733 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1734 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1737 pending_removes.clear ();
1738 pending_length = false;
1739 LengthChanged ();
1740 pending_contents_change = false;
1741 ContentsChanged ();
1746 /***********************************************************************
1747 FINDING THINGS
1748 **********************************************************************/
1750 Playlist::RegionList *
1751 Playlist::regions_at (framepos_t frame)
1754 RegionLock rlock (this);
1755 return find_regions_at (frame);
1758 uint32_t
1759 Playlist::count_regions_at (framepos_t frame) const
1761 RegionLock rlock (const_cast<Playlist*>(this));
1762 uint32_t cnt = 0;
1764 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1765 if ((*i)->covers (frame)) {
1766 cnt++;
1770 return cnt;
1773 boost::shared_ptr<Region>
1774 Playlist::top_region_at (framepos_t frame)
1777 RegionLock rlock (this);
1778 RegionList *rlist = find_regions_at (frame);
1779 boost::shared_ptr<Region> region;
1781 if (rlist->size()) {
1782 RegionSortByLayer cmp;
1783 rlist->sort (cmp);
1784 region = rlist->back();
1787 delete rlist;
1788 return region;
1791 boost::shared_ptr<Region>
1792 Playlist::top_unmuted_region_at (framepos_t frame)
1795 RegionLock rlock (this);
1796 RegionList *rlist = find_regions_at (frame);
1798 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1800 RegionList::iterator tmp = i;
1801 ++tmp;
1803 if ((*i)->muted()) {
1804 rlist->erase (i);
1807 i = tmp;
1810 boost::shared_ptr<Region> region;
1812 if (rlist->size()) {
1813 RegionSortByLayer cmp;
1814 rlist->sort (cmp);
1815 region = rlist->back();
1818 delete rlist;
1819 return region;
1822 Playlist::RegionList*
1823 Playlist::regions_to_read (framepos_t start, framepos_t end)
1825 /* Caller must hold lock */
1827 RegionList covering;
1828 set<framepos_t> to_check;
1829 set<boost::shared_ptr<Region> > unique;
1831 to_check.insert (start);
1832 to_check.insert (end);
1834 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1836 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1838 /* find all/any regions that span start+end */
1840 switch ((*i)->coverage (start, end)) {
1841 case OverlapNone:
1842 break;
1844 case OverlapInternal:
1845 covering.push_back (*i);
1846 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1847 break;
1849 case OverlapStart:
1850 to_check.insert ((*i)->position());
1851 if ((*i)->position() != 0) {
1852 to_check.insert ((*i)->position()-1);
1854 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1855 covering.push_back (*i);
1856 break;
1858 case OverlapEnd:
1859 to_check.insert ((*i)->last_frame());
1860 to_check.insert ((*i)->last_frame()+1);
1861 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1862 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1863 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1864 covering.push_back (*i);
1865 break;
1867 case OverlapExternal:
1868 covering.push_back (*i);
1869 to_check.insert ((*i)->position());
1870 if ((*i)->position() != 0) {
1871 to_check.insert ((*i)->position()-1);
1873 to_check.insert ((*i)->last_frame());
1874 to_check.insert ((*i)->last_frame()+1);
1875 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1876 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1877 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1878 break;
1881 /* don't go too far */
1883 if ((*i)->position() > end) {
1884 break;
1888 RegionList* rlist = new RegionList;
1890 /* find all the regions that cover each position .... */
1892 if (covering.size() == 1) {
1894 rlist->push_back (covering.front());
1895 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1897 } else {
1899 RegionList here;
1900 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1902 here.clear ();
1904 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1906 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1908 if ((*x)->covers (*t)) {
1909 here.push_back (*x);
1910 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1911 (*x)->name(),
1912 (*t)));
1913 } else {
1914 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1915 (*x)->name(),
1916 (*t)));
1921 RegionSortByLayer cmp;
1922 here.sort (cmp);
1924 /* ... and get the top/transparent regions at "here" */
1926 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1928 unique.insert (*c);
1930 if ((*c)->opaque()) {
1932 /* the other regions at this position are hidden by this one */
1933 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1934 (*c)->name()));
1935 break;
1940 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1941 rlist->push_back (*s);
1944 if (rlist->size() > 1) {
1945 /* now sort by time order */
1947 RegionSortByPosition cmp;
1948 rlist->sort (cmp);
1952 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1954 return rlist;
1957 Playlist::RegionList *
1958 Playlist::find_regions_at (framepos_t frame)
1960 /* Caller must hold lock */
1962 RegionList *rlist = new RegionList;
1964 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1965 if ((*i)->covers (frame)) {
1966 rlist->push_back (*i);
1970 return rlist;
1973 Playlist::RegionList *
1974 Playlist::regions_touched (framepos_t start, framepos_t end)
1976 RegionLock rlock (this);
1977 RegionList *rlist = new RegionList;
1979 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1980 if ((*i)->coverage (start, end) != OverlapNone) {
1981 rlist->push_back (*i);
1985 return rlist;
1988 framepos_t
1989 Playlist::find_next_transient (framepos_t from, int dir)
1991 RegionLock rlock (this);
1992 AnalysisFeatureList points;
1993 AnalysisFeatureList these_points;
1995 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1996 if (dir > 0) {
1997 if ((*i)->last_frame() < from) {
1998 continue;
2000 } else {
2001 if ((*i)->first_frame() > from) {
2002 continue;
2006 (*i)->get_transients (these_points);
2008 /* add first frame, just, err, because */
2010 these_points.push_back ((*i)->first_frame());
2012 points.insert (points.end(), these_points.begin(), these_points.end());
2013 these_points.clear ();
2016 if (points.empty()) {
2017 return -1;
2020 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
2021 bool reached = false;
2023 if (dir > 0) {
2024 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
2025 if ((*x) >= from) {
2026 reached = true;
2029 if (reached && (*x) > from) {
2030 return *x;
2033 } else {
2034 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2035 if ((*x) <= from) {
2036 reached = true;
2039 if (reached && (*x) < from) {
2040 return *x;
2045 return -1;
2048 boost::shared_ptr<Region>
2049 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2051 RegionLock rlock (this);
2052 boost::shared_ptr<Region> ret;
2053 framepos_t closest = max_framepos;
2055 bool end_iter = false;
2057 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2059 if(end_iter) break;
2061 frameoffset_t distance;
2062 boost::shared_ptr<Region> r = (*i);
2063 framepos_t pos = 0;
2065 switch (point) {
2066 case Start:
2067 pos = r->first_frame ();
2068 break;
2069 case End:
2070 pos = r->last_frame ();
2071 break;
2072 case SyncPoint:
2073 pos = r->sync_position ();
2074 break;
2077 switch (dir) {
2078 case 1: /* forwards */
2080 if (pos > frame) {
2081 if ((distance = pos - frame) < closest) {
2082 closest = distance;
2083 ret = r;
2084 end_iter = true;
2088 break;
2090 default: /* backwards */
2092 if (pos < frame) {
2093 if ((distance = frame - pos) < closest) {
2094 closest = distance;
2095 ret = r;
2098 else {
2099 end_iter = true;
2102 break;
2106 return ret;
2109 framepos_t
2110 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2112 RegionLock rlock (this);
2114 framepos_t closest = max_framepos;
2115 framepos_t ret = -1;
2117 if (dir > 0) {
2119 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2121 boost::shared_ptr<Region> r = (*i);
2122 frameoffset_t distance;
2124 if (r->first_frame() > frame) {
2126 distance = r->first_frame() - frame;
2128 if (distance < closest) {
2129 ret = r->first_frame();
2130 closest = distance;
2134 if (r->last_frame () > frame) {
2136 distance = r->last_frame () - frame;
2138 if (distance < closest) {
2139 ret = r->last_frame ();
2140 closest = distance;
2145 } else {
2147 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2149 boost::shared_ptr<Region> r = (*i);
2150 frameoffset_t distance;
2152 if (r->last_frame() < frame) {
2154 distance = frame - r->last_frame();
2156 if (distance < closest) {
2157 ret = r->last_frame();
2158 closest = distance;
2162 if (r->first_frame() < frame) {
2164 distance = frame - r->first_frame();
2166 if (distance < closest) {
2167 ret = r->first_frame();
2168 closest = distance;
2174 return ret;
2178 /***********************************************************************/
2183 void
2184 Playlist::mark_session_dirty ()
2186 if (!in_set_state && !holding_state ()) {
2187 _session.set_dirty();
2191 void
2192 Playlist::rdiff (vector<Command*>& cmds) const
2194 RegionLock rlock (const_cast<Playlist *> (this));
2195 Stateful::rdiff (cmds);
2198 void
2199 Playlist::clear_owned_changes ()
2201 RegionLock rlock (this);
2202 Stateful::clear_owned_changes ();
2205 void
2206 Playlist::update (const RegionListProperty::ChangeRecord& change)
2208 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2209 name(), change.added.size(), change.removed.size()));
2211 freeze ();
2212 /* add the added regions */
2213 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2214 add_region ((*i), (*i)->position());
2216 /* remove the removed regions */
2217 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2218 remove_region (*i);
2221 thaw ();
2225 Playlist::set_state (const XMLNode& node, int version)
2227 XMLNode *child;
2228 XMLNodeList nlist;
2229 XMLNodeConstIterator niter;
2230 XMLPropertyList plist;
2231 XMLPropertyConstIterator piter;
2232 XMLProperty *prop;
2233 boost::shared_ptr<Region> region;
2234 string region_name;
2236 in_set_state++;
2238 if (node.name() != "Playlist") {
2239 in_set_state--;
2240 return -1;
2243 freeze ();
2245 plist = node.properties();
2247 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2249 prop = *piter;
2251 if (prop->name() == X_("name")) {
2252 _name = prop->value();
2253 _set_sort_id ();
2254 } else if (prop->name() == X_("id")) {
2255 _id = prop->value();
2256 } else if (prop->name() == X_("orig_diskstream_id")) {
2257 _orig_diskstream_id = prop->value ();
2258 } else if (prop->name() == X_("frozen")) {
2259 _frozen = string_is_affirmative (prop->value());
2263 clear (true);
2265 nlist = node.children();
2267 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2269 child = *niter;
2271 if (child->name() == "Region") {
2273 if ((prop = child->property ("id")) == 0) {
2274 error << _("region state node has no ID, ignored") << endmsg;
2275 continue;
2278 ID id = prop->value ();
2280 if ((region = region_by_id (id))) {
2282 region->suspend_property_changes ();
2284 if (region->set_state (*child, version)) {
2285 region->resume_property_changes ();
2286 continue;
2289 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2290 region->suspend_property_changes ();
2291 } else {
2292 error << _("Playlist: cannot create region from XML") << endmsg;
2293 continue;
2297 add_region (region, region->position(), 1.0);
2299 // So that layer_op ordering doesn't get screwed up
2300 region->set_last_layer_op( region->layer());
2301 region->resume_property_changes ();
2305 /* update dependents, which was not done during add_region_internal
2306 due to in_set_state being true
2309 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2310 check_dependents (*r, false);
2313 thaw ();
2314 notify_contents_changed ();
2316 in_set_state--;
2317 first_set_state = false;
2318 return 0;
2321 XMLNode&
2322 Playlist::get_state()
2324 return state (true);
2327 XMLNode&
2328 Playlist::get_template()
2330 return state (false);
2333 /** @param full_state true to include regions in the returned state, otherwise false.
2335 XMLNode&
2336 Playlist::state (bool full_state)
2338 XMLNode *node = new XMLNode (X_("Playlist"));
2339 char buf[64] = "";
2341 node->add_property (X_("id"), id().to_s());
2342 node->add_property (X_("name"), _name);
2343 node->add_property (X_("type"), _type.to_string());
2345 _orig_diskstream_id.print (buf, sizeof (buf));
2346 node->add_property (X_("orig_diskstream_id"), buf);
2347 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2349 if (full_state) {
2350 RegionLock rlock (this, false);
2352 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2353 node->add_child_nocopy ((*i)->get_state());
2357 if (_extra_xml) {
2358 node->add_child_copy (*_extra_xml);
2361 return *node;
2364 bool
2365 Playlist::empty() const
2367 RegionLock rlock (const_cast<Playlist *>(this), false);
2368 return regions.empty();
2371 uint32_t
2372 Playlist::n_regions() const
2374 RegionLock rlock (const_cast<Playlist *>(this), false);
2375 return regions.size();
2378 pair<framepos_t, framepos_t>
2379 Playlist::get_extent () const
2381 RegionLock rlock (const_cast<Playlist *>(this), false);
2382 return _get_extent ();
2385 pair<framepos_t, framepos_t>
2386 Playlist::_get_extent () const
2388 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2390 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2391 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2392 if (e.first < ext.first) {
2393 ext.first = e.first;
2395 if (e.second > ext.second) {
2396 ext.second = e.second;
2400 return ext;
2403 string
2404 Playlist::bump_name (string name, Session &session)
2406 string newname = name;
2408 do {
2409 newname = bump_name_once (newname, '.');
2410 } while (session.playlists->by_name (newname)!=NULL);
2412 return newname;
2416 layer_t
2417 Playlist::top_layer() const
2419 RegionLock rlock (const_cast<Playlist *> (this));
2420 layer_t top = 0;
2422 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2423 top = max (top, (*i)->layer());
2425 return top;
2428 void
2429 Playlist::set_edit_mode (EditMode mode)
2431 _edit_mode = mode;
2434 /********************
2435 * Region Layering
2436 ********************/
2438 void
2439 Playlist::relayer ()
2441 /* never compute layers when changing state for undo/redo or setting from XML */
2443 if (in_update || in_set_state) {
2444 return;
2447 bool changed = false;
2449 /* Build up a new list of regions on each layer, stored in a set of lists
2450 each of which represent some period of time on some layer. The idea
2451 is to avoid having to search the entire region list to establish whether
2452 each region overlaps another */
2454 /* how many pieces to divide this playlist's time up into */
2455 int const divisions = 512;
2457 /* find the start and end positions of the regions on this playlist */
2458 framepos_t start = INT64_MAX;
2459 framepos_t end = 0;
2460 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2461 start = min (start, (*i)->position());
2462 end = max (end, (*i)->position() + (*i)->length());
2465 /* hence the size of each time division */
2466 double const division_size = (end - start) / double (divisions);
2468 vector<vector<RegionList> > layers;
2469 layers.push_back (vector<RegionList> (divisions));
2471 /* we want to go through regions from desired lowest to desired highest layer,
2472 which depends on the layer model
2475 RegionList copy = regions.rlist();
2477 /* sort according to the model and the layering mode that we're in */
2479 if (_explicit_relayering) {
2481 copy.sort (RegionSortByLayerWithPending ());
2483 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2485 copy.sort (RegionSortByLastLayerOp ());
2490 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2492 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2493 (*i)->set_pending_explicit_relayer (false);
2495 /* find the time divisions that this region covers; if there are no regions on the list,
2496 division_size will equal 0 and in this case we'll just say that
2497 start_division = end_division = 0.
2499 int start_division = 0;
2500 int end_division = 0;
2502 if (division_size > 0) {
2503 start_division = floor ( ((*i)->position() - start) / division_size);
2504 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2505 if (end_division == divisions) {
2506 end_division--;
2510 assert (divisions == 0 || end_division < divisions);
2512 /* find the lowest layer that this region can go on */
2513 size_t j = layers.size();
2514 while (j > 0) {
2515 /* try layer j - 1; it can go on if it overlaps no other region
2516 that is already on that layer
2519 bool overlap = false;
2520 for (int k = start_division; k <= end_division; ++k) {
2521 RegionList::iterator l = layers[j-1][k].begin ();
2522 while (l != layers[j-1][k].end()) {
2523 if ((*l)->overlap_equivalent (*i)) {
2524 overlap = true;
2525 break;
2527 l++;
2530 if (overlap) {
2531 break;
2535 if (overlap) {
2536 /* overlap, so we must use layer j */
2537 break;
2540 --j;
2543 if (j == layers.size()) {
2544 /* we need a new layer for this region */
2545 layers.push_back (vector<RegionList> (divisions));
2548 /* put a reference to this region in each of the divisions that it exists in */
2549 for (int k = start_division; k <= end_division; ++k) {
2550 layers[j][k].push_back (*i);
2553 if ((*i)->layer() != j) {
2554 changed = true;
2557 (*i)->set_layer (j);
2560 if (changed) {
2561 notify_layering_changed ();
2565 /* XXX these layer functions are all deprecated */
2567 void
2568 Playlist::raise_region (boost::shared_ptr<Region> region)
2570 uint32_t top = regions.size() - 1;
2571 layer_t target = region->layer() + 1U;
2573 if (target >= top) {
2574 /* its already at the effective top */
2575 return;
2578 move_region_to_layer (target, region, 1);
2581 void
2582 Playlist::lower_region (boost::shared_ptr<Region> region)
2584 if (region->layer() == 0) {
2585 /* its already at the bottom */
2586 return;
2589 layer_t target = region->layer() - 1U;
2591 move_region_to_layer (target, region, -1);
2594 void
2595 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2597 /* does nothing useful if layering mode is later=higher */
2598 switch (_session.config.get_layer_model()) {
2599 case LaterHigher:
2600 return;
2601 default:
2602 break;
2605 layer_t top = regions.size() - 1;
2607 if (region->layer() >= top) {
2608 /* already on the top */
2609 return;
2612 move_region_to_layer (top, region, 1);
2613 /* mark the region's last_layer_op as now, so that it remains on top when
2614 doing future relayers (until something else takes over)
2616 timestamp_layer_op (region);
2619 void
2620 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2622 /* does nothing useful if layering mode is later=higher */
2623 switch (_session.config.get_layer_model()) {
2624 case LaterHigher:
2625 return;
2626 default:
2627 break;
2630 if (region->layer() == 0) {
2631 /* already on the bottom */
2632 return;
2635 move_region_to_layer (0, region, -1);
2636 /* force region's last layer op to zero so that it stays at the bottom
2637 when doing future relayers
2639 region->set_last_layer_op (0);
2643 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2645 RegionList::iterator i;
2646 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2647 list<LayerInfo> layerinfo;
2650 RegionLock rlock (const_cast<Playlist *> (this));
2652 for (i = regions.begin(); i != regions.end(); ++i) {
2654 if (region == *i) {
2655 continue;
2658 layer_t dest;
2660 if (dir > 0) {
2662 /* region is moving up, move all regions on intermediate layers
2663 down 1
2666 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2667 dest = (*i)->layer() - 1;
2668 } else {
2669 /* not affected */
2670 continue;
2672 } else {
2674 /* region is moving down, move all regions on intermediate layers
2675 up 1
2678 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2679 dest = (*i)->layer() + 1;
2680 } else {
2681 /* not affected */
2682 continue;
2686 LayerInfo newpair;
2688 newpair.first = *i;
2689 newpair.second = dest;
2691 layerinfo.push_back (newpair);
2695 freeze ();
2697 /* now reset the layers without holding the region lock */
2699 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2700 x->first->set_layer (x->second);
2703 region->set_layer (target_layer);
2705 /* now check all dependents, since we changed the layering */
2707 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2708 check_dependents (x->first, false);
2711 check_dependents (region, false);
2712 notify_layering_changed ();
2714 thaw ();
2716 return 0;
2719 void
2720 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2722 RegionList::iterator i;
2723 bool moved = false;
2725 _nudging = true;
2728 RegionLock rlock (const_cast<Playlist *> (this));
2730 for (i = regions.begin(); i != regions.end(); ++i) {
2732 if ((*i)->position() >= start) {
2734 framepos_t new_pos;
2736 if (forwards) {
2738 if ((*i)->last_frame() > max_framepos - distance) {
2739 new_pos = max_framepos - (*i)->length();
2740 } else {
2741 new_pos = (*i)->position() + distance;
2744 } else {
2746 if ((*i)->position() > distance) {
2747 new_pos = (*i)->position() - distance;
2748 } else {
2749 new_pos = 0;
2753 (*i)->set_position (new_pos, this);
2754 moved = true;
2759 if (moved) {
2760 _nudging = false;
2761 notify_length_changed ();
2766 bool
2767 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2769 RegionLock rlock (const_cast<Playlist*> (this));
2771 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2772 if ((*r)->uses_source (src)) {
2773 return true;
2777 return false;
2780 boost::shared_ptr<Region>
2781 Playlist::find_region (const ID& id) const
2783 RegionLock rlock (const_cast<Playlist*> (this));
2785 /* searches all regions currently in use by the playlist */
2787 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2788 if ((*i)->id() == id) {
2789 return *i;
2793 return boost::shared_ptr<Region> ();
2796 uint32_t
2797 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2799 RegionLock rlock (const_cast<Playlist*> (this));
2800 uint32_t cnt = 0;
2802 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2803 if ((*i) == r) {
2804 cnt++;
2808 return cnt;
2811 boost::shared_ptr<Region>
2812 Playlist::region_by_id (const ID& id) const
2814 /* searches all regions ever added to this playlist */
2816 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2817 if ((*i)->id() == id) {
2818 return *i;
2821 return boost::shared_ptr<Region> ();
2824 void
2825 Playlist::dump () const
2827 boost::shared_ptr<Region> r;
2829 cerr << "Playlist \"" << _name << "\" " << endl
2830 << regions.size() << " regions "
2831 << endl;
2833 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2834 r = *i;
2835 cerr << " " << r->name() << " ["
2836 << r->start() << "+" << r->length()
2837 << "] at "
2838 << r->position()
2839 << " on layer "
2840 << r->layer ()
2841 << endl;
2845 void
2846 Playlist::set_frozen (bool yn)
2848 _frozen = yn;
2851 void
2852 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2854 region->set_last_layer_op (++layer_op_counter);
2858 void
2859 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2861 bool moved = false;
2863 if (region->locked()) {
2864 return;
2867 _shuffling = true;
2870 RegionLock rlock (const_cast<Playlist*> (this));
2873 if (dir > 0) {
2875 RegionList::iterator next;
2877 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2878 if ((*i) == region) {
2879 next = i;
2880 ++next;
2882 if (next != regions.end()) {
2884 if ((*next)->locked()) {
2885 break;
2888 framepos_t new_pos;
2890 if ((*next)->position() != region->last_frame() + 1) {
2891 /* they didn't used to touch, so after shuffle,
2892 just have them swap positions.
2894 new_pos = (*next)->position();
2895 } else {
2896 /* they used to touch, so after shuffle,
2897 make sure they still do. put the earlier
2898 region where the later one will end after
2899 it is moved.
2901 new_pos = region->position() + (*next)->length();
2904 (*next)->set_position (region->position(), this);
2905 region->set_position (new_pos, this);
2907 /* avoid a full sort */
2909 regions.erase (i); // removes the region from the list */
2910 next++;
2911 regions.insert (next, region); // adds it back after next
2913 moved = true;
2915 break;
2918 } else {
2920 RegionList::iterator prev = regions.end();
2922 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2923 if ((*i) == region) {
2925 if (prev != regions.end()) {
2927 if ((*prev)->locked()) {
2928 break;
2931 framepos_t new_pos;
2932 if (region->position() != (*prev)->last_frame() + 1) {
2933 /* they didn't used to touch, so after shuffle,
2934 just have them swap positions.
2936 new_pos = region->position();
2937 } else {
2938 /* they used to touch, so after shuffle,
2939 make sure they still do. put the earlier
2940 one where the later one will end after
2942 new_pos = (*prev)->position() + region->length();
2945 region->set_position ((*prev)->position(), this);
2946 (*prev)->set_position (new_pos, this);
2948 /* avoid a full sort */
2950 regions.erase (i); // remove region
2951 regions.insert (prev, region); // insert region before prev
2953 moved = true;
2956 break;
2962 _shuffling = false;
2964 if (moved) {
2966 relayer ();
2967 check_dependents (region, false);
2969 notify_contents_changed();
2974 bool
2975 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2977 RegionLock rlock (const_cast<Playlist*> (this));
2979 if (regions.size() > 1) {
2980 return true;
2983 return false;
2986 void
2987 Playlist::update_after_tempo_map_change ()
2989 RegionLock rlock (const_cast<Playlist*> (this));
2990 RegionList copy (regions.rlist());
2992 freeze ();
2994 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2995 (*i)->update_position_after_tempo_map_change ();
2998 thaw ();
3001 void
3002 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
3004 RegionLock rl (this, false);
3005 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
3006 s (*i);
3010 void
3011 Playlist::set_explicit_relayering (bool e)
3013 if (e == false && _explicit_relayering == true) {
3015 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
3016 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
3017 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
3018 at this point would keep regions on the same layers.
3020 From then on in, it's just you and your towel.
3023 RegionLock rl (this);
3024 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
3025 (*i)->set_last_layer_op ((*i)->layer ());
3029 _explicit_relayering = e;
3033 bool
3034 Playlist::has_region_at (framepos_t const p) const
3036 RegionLock (const_cast<Playlist *> (this));
3038 RegionList::const_iterator i = regions.begin ();
3039 while (i != regions.end() && !(*i)->covers (p)) {
3040 ++i;
3043 return (i != regions.end());
3046 /** Remove any region that uses a given source */
3047 void
3048 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
3050 RegionLock rl (this);
3052 RegionList::iterator i = regions.begin();
3053 while (i != regions.end()) {
3054 RegionList::iterator j = i;
3055 ++j;
3057 if ((*i)->uses_source (s)) {
3058 remove_region_internal (*i);
3061 i = j;
3065 /** Look from a session frame time and find the start time of the next region
3066 * which is on the top layer of this playlist.
3067 * @param t Time to look from.
3068 * @return Position of next top-layered region, or max_framepos if there isn't one.
3070 framepos_t
3071 Playlist::find_next_top_layer_position (framepos_t t) const
3073 RegionLock rlock (const_cast<Playlist *> (this));
3075 layer_t const top = top_layer ();
3077 RegionList copy = regions.rlist ();
3078 copy.sort (RegionSortByPosition ());
3080 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3081 if ((*i)->position() >= t && (*i)->layer() == top) {
3082 return (*i)->position();
3086 return max_framepos;
3089 void
3090 Playlist::join (const RegionList& r, const std::string& name)
3092 PropertyList plist;
3093 uint32_t channels = 0;
3094 uint32_t layer = 0;
3095 framepos_t earliest_position = max_framepos;
3097 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, name, true);
3099 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3100 earliest_position = min (earliest_position, (*i)->position());
3103 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3105 /* copy the region */
3107 boost::shared_ptr<Region> original_region = (*i);
3108 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3110 /* make position relative to zero */
3112 pl->add_region (copied_region, original_region->position() - earliest_position);
3114 /* use the maximum number of channels for any region */
3116 channels = max (channels, original_region->n_channels());
3118 /* it will go above the layer of the highest existing region */
3120 layer = max (layer, original_region->layer());
3123 /* now create a new PlaylistSource for each channel in the new playlist */
3125 SourceList sources;
3126 pair<framepos_t,framepos_t> extent = pl->get_extent();
3128 for (uint32_t chn = 0; chn < channels; ++chn) {
3129 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, name, chn, 0, extent.second, false, false));
3132 /* now a new region using the list of sources */
3134 plist.add (Properties::start, 0);
3135 plist.add (Properties::length, extent.second);
3136 plist.add (Properties::name, name);
3137 plist.add (Properties::layer, layer+1);
3139 boost::shared_ptr<Region> compound_region = RegionFactory::create (sources, plist, true);
3141 /* remove all the selected regions from the current playlist
3144 freeze ();
3146 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3147 remove_region (*i);
3150 /* add the new region at the right location */
3152 add_region (compound_region, earliest_position);
3154 thaw ();
3157 uint32_t
3158 Playlist::max_source_level () const
3160 RegionLock rlock (const_cast<Playlist *> (this));
3161 uint32_t lvl = 0;
3163 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3164 lvl = max (lvl, (*i)->max_source_level());
3167 return lvl;
3171 uint32_t
3172 Playlist::count_joined_regions () const
3174 RegionLock rlock (const_cast<Playlist *> (this));
3175 uint32_t cnt = 0;
3177 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3178 if ((*i)->max_source_level() > 0) {
3179 cnt++;
3183 return cnt;