Don't move automation to follow region when a region has only been trimmed rather...
[ArdourMidi.git] / libs / ardour / playlist.cc
blobe55b82c06b819a6f34528638f1acd6cd276547e3
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 #define __STDC_LIMIT_MACROS
21 #include <stdint.h>
23 #include <set>
24 #include <fstream>
25 #include <algorithm>
26 #include <unistd.h>
27 #include <cerrno>
28 #include <string>
29 #include <climits>
31 #include <boost/lexical_cast.hpp>
33 #include "pbd/failed_constructor.h"
34 #include "pbd/stateful_diff_command.h"
35 #include "pbd/xml++.h"
37 #include "ardour/debug.h"
38 #include "ardour/playlist.h"
39 #include "ardour/session.h"
40 #include "ardour/region.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/playlist_factory.h"
43 #include "ardour/transient_detector.h"
44 #include "ardour/session_playlists.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", Properties::regions.property_id));
109 RegionListProperty::RegionListProperty (Playlist& pl)
110 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
111 , _playlist (pl)
115 boost::shared_ptr<Region>
116 RegionListProperty::lookup_id (const ID& id)
118 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
120 if (!ret) {
121 ret = RegionFactory::region_by_id (id);
124 return ret;
127 RegionListProperty*
128 RegionListProperty::copy_for_history () const
130 RegionListProperty* copy = new RegionListProperty (_playlist);
131 /* this is all we need */
132 copy->_change = _change;
133 return copy;
136 void
137 RegionListProperty::diff (PropertyList& undo, PropertyList& redo, Command* cmd) const
139 if (changed()) {
140 /* list of the removed/added regions since clear_history() was last called */
141 RegionListProperty* a = copy_for_history ();
143 /* the same list, but with removed/added lists swapped (for undo purposes) */
144 RegionListProperty* b = copy_for_history ();
145 b->invert_changes ();
147 if (cmd) {
148 /* whenever one of the regions emits DropReferences, make sure
149 that the Destructible we've been told to notify hears about
150 it. the Destructible is likely to be the Command being built
151 with this diff().
154 for (set<boost::shared_ptr<Region> >::iterator i = a->change().added.begin(); i != a->change().added.end(); ++i) {
155 (*i)->DropReferences.connect_same_thread (*cmd, boost::bind (&Destructible::drop_references, cmd));
159 undo.add (b);
160 redo.add (a);
164 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
165 : SessionObject(sess, nom)
166 , regions (*this)
167 , _type(type)
169 init (hide);
170 first_set_state = false;
171 _name = nom;
172 _set_sort_id ();
176 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
177 : SessionObject(sess, "unnamed playlist")
178 , regions (*this)
179 , _type(type)
182 #ifndef NDEBUG
183 const XMLProperty* prop = node.property("type");
184 assert(!prop || DataType(prop->value()) == _type);
185 #endif
187 init (hide);
188 _name = "unnamed"; /* reset by set_state */
189 _set_sort_id ();
191 /* set state called by derived class */
194 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
195 : SessionObject(other->_session, namestr)
196 , regions (*this)
197 , _type(other->_type)
198 , _orig_diskstream_id (other->_orig_diskstream_id)
200 init (hide);
202 RegionList tmp;
203 other->copy_regions (tmp);
205 in_set_state++;
207 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
208 add_region_internal( (*x), (*x)->position());
211 in_set_state--;
213 _splicing = other->_splicing;
214 _nudging = other->_nudging;
215 _edit_mode = other->_edit_mode;
217 in_set_state = 0;
218 first_set_state = false;
219 in_flush = false;
220 in_partition = false;
221 subcnt = 0;
222 _read_data_count = 0;
223 _frozen = other->_frozen;
225 layer_op_counter = other->layer_op_counter;
226 freeze_length = other->freeze_length;
229 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
230 : SessionObject(other->_session, str)
231 , regions (*this)
232 , _type(other->_type)
233 , _orig_diskstream_id (other->_orig_diskstream_id)
235 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
237 framepos_t end = start + cnt - 1;
239 init (hide);
241 in_set_state++;
243 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
245 boost::shared_ptr<Region> region;
246 boost::shared_ptr<Region> new_region;
247 frameoffset_t offset = 0;
248 framepos_t position = 0;
249 framecnt_t len = 0;
250 string new_name;
251 OverlapType overlap;
253 region = *i;
255 overlap = region->coverage (start, end);
257 switch (overlap) {
258 case OverlapNone:
259 continue;
261 case OverlapInternal:
262 offset = start - region->position();
263 position = 0;
264 len = cnt;
265 break;
267 case OverlapStart:
268 offset = 0;
269 position = region->position() - start;
270 len = end - region->position();
271 break;
273 case OverlapEnd:
274 offset = start - region->position();
275 position = 0;
276 len = region->length() - offset;
277 break;
279 case OverlapExternal:
280 offset = 0;
281 position = region->position() - start;
282 len = region->length();
283 break;
286 RegionFactory::region_name (new_name, region->name(), false);
288 PropertyList plist;
290 plist.add (Properties::start, region->start() + offset);
291 plist.add (Properties::length, len);
292 plist.add (Properties::name, new_name);
293 plist.add (Properties::layer, region->layer());
295 new_region = RegionFactory::RegionFactory::create (region, plist);
297 add_region_internal (new_region, position);
300 in_set_state--;
301 first_set_state = false;
303 /* this constructor does NOT notify others (session) */
306 void
307 Playlist::use ()
309 ++_refcnt;
310 InUse (true); /* EMIT SIGNAL */
313 void
314 Playlist::release ()
316 if (_refcnt > 0) {
317 _refcnt--;
320 if (_refcnt == 0) {
321 InUse (false); /* EMIT SIGNAL */
325 void
326 Playlist::copy_regions (RegionList& newlist) const
328 RegionLock rlock (const_cast<Playlist *> (this));
330 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
331 newlist.push_back (RegionFactory::RegionFactory::create (*i));
335 void
336 Playlist::init (bool hide)
338 add_property (regions);
339 _xml_node_name = X_("Playlist");
341 g_atomic_int_set (&block_notifications, 0);
342 g_atomic_int_set (&ignore_state_changes, 0);
343 pending_contents_change = false;
344 pending_length = false;
345 pending_layering = false;
346 first_set_state = true;
347 _refcnt = 0;
348 _hidden = hide;
349 _splicing = false;
350 _shuffling = false;
351 _nudging = false;
352 in_set_state = 0;
353 in_update = false;
354 _edit_mode = Config->get_edit_mode();
355 in_flush = false;
356 in_partition = false;
357 subcnt = 0;
358 _read_data_count = 0;
359 _frozen = false;
360 layer_op_counter = 0;
361 freeze_length = 0;
362 _explicit_relayering = false;
364 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
365 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
367 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
370 Playlist::~Playlist ()
372 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
375 RegionLock rl (this);
377 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
378 (*i)->set_playlist (boost::shared_ptr<Playlist>());
382 /* GoingAway must be emitted by derived classes */
385 void
386 Playlist::_set_sort_id ()
389 Playlists are given names like <track name>.<id>
390 or <track name>.<edit group name>.<id> where id
391 is an integer. We extract the id and sort by that.
394 size_t dot_position = _name.val().find_last_of(".");
396 if (dot_position == string::npos) {
397 _sort_id = 0;
398 } else {
399 string t = _name.val().substr(dot_position + 1);
401 try {
402 _sort_id = boost::lexical_cast<int>(t);
405 catch (boost::bad_lexical_cast e) {
406 _sort_id = 0;
411 bool
412 Playlist::set_name (const string& str)
414 /* in a typical situation, a playlist is being used
415 by one diskstream and also is referenced by the
416 Session. if there are more references than that,
417 then don't change the name.
420 if (_refcnt > 2) {
421 return false;
424 bool ret = SessionObject::set_name(str);
425 if (ret) {
426 _set_sort_id ();
428 return ret;
431 /***********************************************************************
432 CHANGE NOTIFICATION HANDLING
434 Notifications must be delayed till the region_lock is released. This
435 is necessary because handlers for the signals may need to acquire
436 the lock (e.g. to read from the playlist).
437 ***********************************************************************/
439 void
440 Playlist::begin_undo ()
442 in_update = true;
443 freeze ();
446 void
447 Playlist::end_undo ()
449 thaw (true);
450 in_update = false;
453 void
454 Playlist::freeze ()
456 delay_notifications ();
457 g_atomic_int_inc (&ignore_state_changes);
460 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
461 void
462 Playlist::thaw (bool from_undo)
464 g_atomic_int_dec_and_test (&ignore_state_changes);
465 release_notifications (from_undo);
469 void
470 Playlist::delay_notifications ()
472 g_atomic_int_inc (&block_notifications);
473 freeze_length = _get_extent().second;
476 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
477 void
478 Playlist::release_notifications (bool from_undo)
480 if (g_atomic_int_dec_and_test (&block_notifications)) {
481 flush_notifications (from_undo);
486 void
487 Playlist::notify_contents_changed ()
489 if (holding_state ()) {
490 pending_contents_change = true;
491 } else {
492 pending_contents_change = false;
493 ContentsChanged(); /* EMIT SIGNAL */
497 void
498 Playlist::notify_layering_changed ()
500 if (holding_state ()) {
501 pending_layering = true;
502 } else {
503 pending_layering = false;
504 LayeringChanged(); /* EMIT SIGNAL */
508 void
509 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
511 if (holding_state ()) {
512 pending_removes.insert (r);
513 pending_contents_change = true;
514 pending_length = true;
515 } else {
516 /* this might not be true, but we have to act
517 as though it could be.
519 pending_length = false;
520 LengthChanged (); /* EMIT SIGNAL */
521 pending_contents_change = false;
522 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
523 ContentsChanged (); /* EMIT SIGNAL */
527 void
528 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
530 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
532 if (holding_state ()) {
534 pending_range_moves.push_back (move);
536 } else {
538 list< Evoral::RangeMove<framepos_t> > m;
539 m.push_back (move);
540 RangesMoved (m, false);
545 void
546 Playlist::notify_region_added (boost::shared_ptr<Region> r)
548 /* the length change might not be true, but we have to act
549 as though it could be.
552 if (holding_state()) {
553 pending_adds.insert (r);
554 pending_contents_change = true;
555 pending_length = true;
556 } else {
557 r->clear_history ();
558 pending_length = false;
559 LengthChanged (); /* EMIT SIGNAL */
560 pending_contents_change = false;
561 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
562 ContentsChanged (); /* EMIT SIGNAL */
566 void
567 Playlist::notify_length_changed ()
569 if (holding_state ()) {
570 pending_length = true;
571 } else {
572 pending_length = false;
573 LengthChanged(); /* EMIT SIGNAL */
574 pending_contents_change = false;
575 ContentsChanged (); /* EMIT SIGNAL */
579 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
580 void
581 Playlist::flush_notifications (bool from_undo)
583 set<boost::shared_ptr<Region> > dependent_checks_needed;
584 set<boost::shared_ptr<Region> >::iterator s;
585 uint32_t regions_changed = false;
586 bool check_length = false;
587 framecnt_t old_length = 0;
589 if (in_flush) {
590 return;
593 in_flush = true;
595 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
596 regions_changed = true;
597 if (!pending_length) {
598 old_length = _get_extent ().second;
599 check_length = true;
603 /* we have no idea what order the regions ended up in pending
604 bounds (it could be based on selection order, for example).
605 so, to preserve layering in the "most recently moved is higher"
606 model, sort them by existing layer, then timestamp them.
609 // RegionSortByLayer cmp;
610 // pending_bounds.sort (cmp);
612 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
613 if (_session.config.get_layer_model() == MoveAddHigher) {
614 timestamp_layer_op (*r);
616 dependent_checks_needed.insert (*r);
619 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
620 remove_dependents (*s);
621 // cerr << _name << " sends RegionRemoved\n";
622 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
625 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
626 // cerr << _name << " sends RegionAdded\n";
627 /* don't emit RegionAdded signal until relayering is done,
628 so that the region is fully setup by the time
629 anyone hear's that its been added
631 dependent_checks_needed.insert (*s);
634 if (check_length) {
635 if (old_length != _get_extent().second) {
636 pending_length = true;
637 // cerr << _name << " length has changed\n";
641 if (pending_length || (freeze_length != _get_extent().second)) {
642 pending_length = false;
643 // cerr << _name << " sends LengthChanged\n";
644 LengthChanged(); /* EMIT SIGNAL */
647 if (regions_changed || pending_contents_change) {
648 if (!in_set_state) {
649 relayer ();
651 pending_contents_change = false;
652 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
653 ContentsChanged (); /* EMIT SIGNAL */
654 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
657 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
658 (*s)->clear_history ();
659 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
662 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
663 check_dependents (*s, false);
666 if (!pending_range_moves.empty ()) {
667 RangesMoved (pending_range_moves, from_undo);
670 clear_pending ();
672 in_flush = false;
675 void
676 Playlist::clear_pending ()
678 pending_adds.clear ();
679 pending_removes.clear ();
680 pending_bounds.clear ();
681 pending_range_moves.clear ();
682 pending_contents_change = false;
683 pending_length = false;
686 /*************************************************************
687 PLAYLIST OPERATIONS
688 *************************************************************/
690 void
691 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
693 RegionLock rlock (this);
694 times = fabs (times);
696 int itimes = (int) floor (times);
698 framepos_t pos = position;
700 if (times == 1 && auto_partition){
701 partition(pos - 1, (pos + region->length()), true);
704 if (itimes >= 1) {
705 add_region_internal (region, pos);
706 pos += region->length();
707 --itimes;
711 /* note that itimes can be zero if we being asked to just
712 insert a single fraction of the region.
715 for (int i = 0; i < itimes; ++i) {
716 boost::shared_ptr<Region> copy = RegionFactory::create (region);
717 add_region_internal (copy, pos);
718 pos += region->length();
721 framecnt_t length = 0;
723 if (floor (times) != times) {
724 length = (framecnt_t) floor (region->length() * (times - floor (times)));
725 string name;
726 RegionFactory::region_name (name, region->name(), false);
729 PropertyList plist;
731 plist.add (Properties::start, region->start());
732 plist.add (Properties::length, length);
733 plist.add (Properties::name, name);
734 plist.add (Properties::layer, region->layer());
736 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
737 add_region_internal (sub, pos);
741 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
744 void
745 Playlist::set_region_ownership ()
747 RegionLock rl (this);
748 RegionList::iterator i;
749 boost::weak_ptr<Playlist> pl (shared_from_this());
751 for (i = regions.begin(); i != regions.end(); ++i) {
752 (*i)->set_playlist (pl);
756 bool
757 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
759 if (region->data_type() != _type){
760 return false;
763 RegionSortByPosition cmp;
765 framecnt_t old_length = 0;
767 if (!holding_state()) {
768 old_length = _get_extent().second;
771 if (!first_set_state) {
772 boost::shared_ptr<Playlist> foo (shared_from_this());
773 region->set_playlist (boost::weak_ptr<Playlist>(foo));
776 region->set_position (position, this);
778 timestamp_layer_op (region);
780 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
781 all_regions.insert (region);
783 possibly_splice_unlocked (position, region->length(), region);
785 if (!holding_state ()) {
786 /* layers get assigned from XML state, and are not reset during undo/redo */
787 relayer ();
790 /* we need to notify the existence of new region before checking dependents. Ick. */
792 notify_region_added (region);
794 if (!holding_state ()) {
796 check_dependents (region, false);
798 if (old_length != _get_extent().second) {
799 notify_length_changed ();
803 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
805 return true;
808 void
809 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
811 RegionLock rlock (this);
813 bool old_sp = _splicing;
814 _splicing = true;
816 remove_region_internal (old);
817 add_region_internal (newr, pos);
819 _splicing = old_sp;
821 possibly_splice_unlocked (pos, old->length() - newr->length());
824 void
825 Playlist::remove_region (boost::shared_ptr<Region> region)
827 RegionLock rlock (this);
828 remove_region_internal (region);
832 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
834 RegionList::iterator i;
835 framecnt_t old_length = 0;
836 int ret = -1;
838 if (!holding_state()) {
839 old_length = _get_extent().second;
842 if (!in_set_state) {
843 /* unset playlist */
844 region->set_playlist (boost::weak_ptr<Playlist>());
847 /* XXX should probably freeze here .... */
849 for (i = regions.begin(); i != regions.end(); ++i) {
850 if (*i == region) {
852 framepos_t pos = (*i)->position();
853 framecnt_t distance = (*i)->length();
855 regions.erase (i);
857 possibly_splice_unlocked (pos, -distance);
859 if (!holding_state ()) {
860 relayer ();
861 remove_dependents (region);
863 if (old_length != _get_extent().second) {
864 notify_length_changed ();
868 notify_region_removed (region);
869 ret = 0;
870 break;
874 return -1;
877 void
878 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
880 if (Config->get_use_overlap_equivalency()) {
881 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
882 if ((*i)->overlap_equivalent (other)) {
883 results.push_back ((*i));
886 } else {
887 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
888 if ((*i)->equivalent (other)) {
889 results.push_back ((*i));
895 void
896 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
898 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
900 if ((*i) && (*i)->region_list_equivalent (other)) {
901 results.push_back (*i);
906 void
907 Playlist::partition (framepos_t start, framepos_t end, bool cut)
909 RegionList thawlist;
911 partition_internal (start, end, cut, thawlist);
913 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
914 (*i)->resume_property_changes ();
918 void
919 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
921 RegionList new_regions;
924 RegionLock rlock (this);
926 boost::shared_ptr<Region> region;
927 boost::shared_ptr<Region> current;
928 string new_name;
929 RegionList::iterator tmp;
930 OverlapType overlap;
931 framepos_t pos1, pos2, pos3, pos4;
933 in_partition = true;
935 /* need to work from a copy, because otherwise the regions we add during the process
936 get operated on as well.
939 RegionList copy = regions.rlist();
941 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
943 tmp = i;
944 ++tmp;
946 current = *i;
948 if (current->first_frame() >= start && current->last_frame() < end) {
950 if (cutting) {
951 remove_region_internal (current);
954 continue;
957 /* coverage will return OverlapStart if the start coincides
958 with the end point. we do not partition such a region,
959 so catch this special case.
962 if (current->first_frame() >= end) {
963 continue;
966 if ((overlap = current->coverage (start, end)) == OverlapNone) {
967 continue;
970 pos1 = current->position();
971 pos2 = start;
972 pos3 = end;
973 pos4 = current->last_frame();
975 if (overlap == OverlapInternal) {
976 /* split: we need 3 new regions, the front, middle and end.
977 cut: we need 2 regions, the front and end.
981 start end
982 ---------------*************************------------
983 P1 P2 P3 P4
984 SPLIT:
985 ---------------*****++++++++++++++++====------------
987 ---------------*****----------------====------------
991 if (!cutting) {
992 /* "middle" ++++++ */
994 RegionFactory::region_name (new_name, current->name(), false);
996 PropertyList plist;
998 plist.add (Properties::start, current->start() + (pos2 - pos1));
999 plist.add (Properties::length, pos3 - pos2);
1000 plist.add (Properties::name, new_name);
1001 plist.add (Properties::layer, regions.size());
1002 plist.add (Properties::automatic, true);
1003 plist.add (Properties::left_of_split, true);
1004 plist.add (Properties::right_of_split, true);
1006 region = RegionFactory::create (current, plist);
1007 add_region_internal (region, start);
1008 new_regions.push_back (region);
1011 /* "end" ====== */
1013 RegionFactory::region_name (new_name, current->name(), false);
1015 PropertyList plist;
1017 plist.add (Properties::start, current->start() + (pos3 - pos1));
1018 plist.add (Properties::length, pos4 - pos3);
1019 plist.add (Properties::name, new_name);
1020 plist.add (Properties::layer, regions.size());
1021 plist.add (Properties::automatic, true);
1022 plist.add (Properties::right_of_split, true);
1024 region = RegionFactory::create (current, plist);
1026 add_region_internal (region, end);
1027 new_regions.push_back (region);
1029 /* "front" ***** */
1031 current->suspend_property_changes ();
1032 thawlist.push_back (current);
1033 current->cut_end (pos2 - 1, this);
1035 } else if (overlap == OverlapEnd) {
1038 start end
1039 ---------------*************************------------
1040 P1 P2 P4 P3
1041 SPLIT:
1042 ---------------**************+++++++++++------------
1043 CUT:
1044 ---------------**************-----------------------
1047 if (!cutting) {
1049 /* end +++++ */
1051 RegionFactory::region_name (new_name, current->name(), false);
1053 PropertyList plist;
1055 plist.add (Properties::start, current->start() + (pos2 - pos1));
1056 plist.add (Properties::length, pos4 - pos2);
1057 plist.add (Properties::name, new_name);
1058 plist.add (Properties::layer, regions.size());
1059 plist.add (Properties::automatic, true);
1060 plist.add (Properties::left_of_split, true);
1062 region = RegionFactory::create (current, plist);
1064 add_region_internal (region, start);
1065 new_regions.push_back (region);
1068 /* front ****** */
1070 current->suspend_property_changes ();
1071 thawlist.push_back (current);
1072 current->cut_end (pos2 - 1, this);
1074 } else if (overlap == OverlapStart) {
1076 /* split: we need 2 regions: the front and the end.
1077 cut: just trim current to skip the cut area
1081 start end
1082 ---------------*************************------------
1083 P2 P1 P3 P4
1085 SPLIT:
1086 ---------------****+++++++++++++++++++++------------
1087 CUT:
1088 -------------------*********************------------
1092 if (!cutting) {
1093 /* front **** */
1094 RegionFactory::region_name (new_name, current->name(), false);
1096 PropertyList plist;
1098 plist.add (Properties::start, current->start());
1099 plist.add (Properties::length, pos3 - pos1);
1100 plist.add (Properties::name, new_name);
1101 plist.add (Properties::layer, regions.size());
1102 plist.add (Properties::automatic, true);
1103 plist.add (Properties::right_of_split, true);
1105 region = RegionFactory::create (current, plist);
1107 add_region_internal (region, pos1);
1108 new_regions.push_back (region);
1111 /* end */
1113 current->suspend_property_changes ();
1114 thawlist.push_back (current);
1115 current->trim_front (pos3, this);
1116 } else if (overlap == OverlapExternal) {
1118 /* split: no split required.
1119 cut: remove the region.
1123 start end
1124 ---------------*************************------------
1125 P2 P1 P3 P4
1127 SPLIT:
1128 ---------------*************************------------
1129 CUT:
1130 ----------------------------------------------------
1134 if (cutting) {
1135 remove_region_internal (current);
1138 new_regions.push_back (current);
1142 in_partition = false;
1145 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1146 check_dependents (*i, false);
1150 boost::shared_ptr<Playlist>
1151 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1153 boost::shared_ptr<Playlist> ret;
1154 boost::shared_ptr<Playlist> pl;
1155 framepos_t start;
1157 if (ranges.empty()) {
1158 return boost::shared_ptr<Playlist>();
1161 start = ranges.front().start;
1163 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1165 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1167 if (i == ranges.begin()) {
1168 ret = pl;
1169 } else {
1171 /* paste the next section into the nascent playlist,
1172 offset to reflect the start of the first range we
1173 chopped.
1176 ret->paste (pl, (*i).start - start, 1.0f);
1180 return ret;
1183 boost::shared_ptr<Playlist>
1184 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1186 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1187 return cut_copy (pmf, ranges, result_is_hidden);
1190 boost::shared_ptr<Playlist>
1191 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1193 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1194 return cut_copy (pmf, ranges, result_is_hidden);
1197 boost::shared_ptr<Playlist>
1198 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1200 boost::shared_ptr<Playlist> the_copy;
1201 RegionList thawlist;
1202 char buf[32];
1204 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1205 string new_name = _name;
1206 new_name += '.';
1207 new_name += buf;
1209 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1210 return boost::shared_ptr<Playlist>();
1213 partition_internal (start, start+cnt-1, true, thawlist);
1215 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1216 (*i)->resume_property_changes();
1219 return the_copy;
1222 boost::shared_ptr<Playlist>
1223 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1225 char buf[32];
1227 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1228 string new_name = _name;
1229 new_name += '.';
1230 new_name += buf;
1232 cnt = min (_get_extent().second - start, cnt);
1233 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1237 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1239 times = fabs (times);
1242 RegionLock rl1 (this);
1243 RegionLock rl2 (other.get());
1245 framecnt_t const old_length = _get_extent().second;
1247 int itimes = (int) floor (times);
1248 framepos_t pos = position;
1249 framecnt_t const shift = other->_get_extent().second;
1250 layer_t top_layer = regions.size();
1252 while (itimes--) {
1253 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1254 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
1256 /* put these new regions on top of all existing ones, but preserve
1257 the ordering they had in the original playlist.
1260 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1261 add_region_internal (copy_of_region, copy_of_region->position() + pos);
1263 pos += shift;
1267 /* XXX shall we handle fractional cases at some point? */
1269 if (old_length != _get_extent().second) {
1270 notify_length_changed ();
1276 return 0;
1280 void
1281 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1283 times = fabs (times);
1285 RegionLock rl (this);
1286 int itimes = (int) floor (times);
1287 framepos_t pos = position + 1;
1289 while (itimes--) {
1290 boost::shared_ptr<Region> copy = RegionFactory::create (region);
1291 add_region_internal (copy, pos);
1292 pos += region->length();
1295 if (floor (times) != times) {
1296 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1297 string name;
1298 RegionFactory::region_name (name, region->name(), false);
1301 PropertyList plist;
1303 plist.add (Properties::start, region->start());
1304 plist.add (Properties::length, length);
1305 plist.add (Properties::name, name);
1307 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1308 add_region_internal (sub, pos);
1313 void
1314 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1316 RegionLock rlock (this);
1317 RegionList copy (regions.rlist());
1318 RegionList fixup;
1320 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1322 if ((*r)->last_frame() < at) {
1323 /* too early */
1324 continue;
1327 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1328 /* intersected region */
1329 if (!move_intersected) {
1330 continue;
1334 /* do not move regions glued to music time - that
1335 has to be done separately.
1338 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1339 fixup.push_back (*r);
1340 continue;
1343 (*r)->set_position ((*r)->position() + distance, this);
1346 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1347 (*r)->recompute_position_from_lock_style ();
1351 void
1352 Playlist::split (framepos_t at)
1354 RegionLock rlock (this);
1355 RegionList copy (regions.rlist());
1357 /* use a copy since this operation can modify the region list
1360 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1361 _split_region (*r, at);
1365 void
1366 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1368 RegionLock rl (this);
1369 _split_region (region, playlist_position);
1372 void
1373 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1375 if (!region->covers (playlist_position)) {
1376 return;
1379 if (region->position() == playlist_position ||
1380 region->last_frame() == playlist_position) {
1381 return;
1384 boost::shared_ptr<Region> left;
1385 boost::shared_ptr<Region> right;
1386 frameoffset_t before;
1387 frameoffset_t after;
1388 string before_name;
1389 string after_name;
1391 /* split doesn't change anything about length, so don't try to splice */
1393 bool old_sp = _splicing;
1394 _splicing = true;
1396 before = playlist_position - region->position();
1397 after = region->length() - before;
1399 RegionFactory::region_name (before_name, region->name(), false);
1402 PropertyList plist;
1404 plist.add (Properties::start, region->start());
1405 plist.add (Properties::length, before);
1406 plist.add (Properties::name, before_name);
1407 plist.add (Properties::left_of_split, true);
1409 left = RegionFactory::create (region, plist);
1412 RegionFactory::region_name (after_name, region->name(), false);
1415 PropertyList plist;
1417 plist.add (Properties::start, region->start() + before);
1418 plist.add (Properties::length, after);
1419 plist.add (Properties::name, after_name);
1420 plist.add (Properties::right_of_split, true);
1422 right = RegionFactory::create (region, plist);
1425 add_region_internal (left, region->position());
1426 add_region_internal (right, region->position() + before);
1428 uint64_t orig_layer_op = region->last_layer_op();
1429 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1430 if ((*i)->last_layer_op() > orig_layer_op) {
1431 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1435 left->set_last_layer_op ( orig_layer_op );
1436 right->set_last_layer_op ( orig_layer_op + 1);
1438 layer_op_counter++;
1440 finalize_split_region (region, left, right);
1442 remove_region_internal (region);
1444 _splicing = old_sp;
1447 void
1448 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1450 if (_splicing || in_set_state) {
1451 /* don't respond to splicing moves or state setting */
1452 return;
1455 if (_edit_mode == Splice) {
1456 splice_locked (at, distance, exclude);
1460 void
1461 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1463 if (_splicing || in_set_state) {
1464 /* don't respond to splicing moves or state setting */
1465 return;
1468 if (_edit_mode == Splice) {
1469 splice_unlocked (at, distance, exclude);
1473 void
1474 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1477 RegionLock rl (this);
1478 core_splice (at, distance, exclude);
1482 void
1483 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1485 core_splice (at, distance, exclude);
1488 void
1489 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1491 _splicing = true;
1493 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1495 if (exclude && (*i) == exclude) {
1496 continue;
1499 if ((*i)->position() >= at) {
1500 framepos_t new_pos = (*i)->position() + distance;
1501 if (new_pos < 0) {
1502 new_pos = 0;
1503 } else if (new_pos >= max_frames - (*i)->length()) {
1504 new_pos = max_frames - (*i)->length();
1507 (*i)->set_position (new_pos, this);
1511 _splicing = false;
1513 notify_length_changed ();
1516 void
1517 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1519 if (in_set_state || _splicing || _nudging || _shuffling) {
1520 return;
1523 if (what_changed.contains (Properties::position)) {
1525 /* remove it from the list then add it back in
1526 the right place again.
1529 RegionSortByPosition cmp;
1531 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1533 if (i == regions.end()) {
1534 /* the region bounds are being modified but its not currently
1535 in the region list. we will use its bounds correctly when/if
1536 it is added
1538 return;
1541 regions.erase (i);
1542 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1545 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1547 frameoffset_t delta = 0;
1549 if (what_changed.contains (Properties::position)) {
1550 delta = region->position() - region->last_position();
1553 if (what_changed.contains (Properties::length)) {
1554 delta += region->length() - region->last_length();
1557 if (delta) {
1558 possibly_splice (region->last_position() + region->last_length(), delta, region);
1561 if (holding_state ()) {
1562 pending_bounds.push_back (region);
1563 } else {
1564 if (_session.config.get_layer_model() == MoveAddHigher) {
1565 /* it moved or changed length, so change the timestamp */
1566 timestamp_layer_op (region);
1569 notify_length_changed ();
1570 relayer ();
1571 check_dependents (region, false);
1576 void
1577 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1579 boost::shared_ptr<Region> region (weak_region.lock());
1581 if (!region) {
1582 return;
1585 /* this makes a virtual call to the right kind of playlist ... */
1587 region_changed (what_changed, region);
1590 bool
1591 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1593 PropertyChange our_interests;
1594 PropertyChange bounds;
1595 PropertyChange pos_and_length;
1596 bool save = false;
1598 if (in_set_state || in_flush) {
1599 return false;
1602 our_interests.add (Properties::muted);
1603 our_interests.add (Properties::layer);
1604 our_interests.add (Properties::opaque);
1606 bounds.add (Properties::start);
1607 bounds.add (Properties::position);
1608 bounds.add (Properties::length);
1610 pos_and_length.add (Properties::position);
1611 pos_and_length.add (Properties::length);
1613 if (what_changed.contains (bounds)) {
1614 region_bounds_changed (what_changed, region);
1615 save = !(_splicing || _nudging);
1618 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1619 check_dependents (region, false);
1622 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1623 notify_region_moved (region);
1627 /* don't notify about layer changes, since we are the only object that can initiate
1628 them, and we notify in ::relayer()
1631 if (what_changed.contains (our_interests)) {
1632 save = true;
1635 return save;
1638 void
1639 Playlist::drop_regions ()
1641 RegionLock rl (this);
1642 regions.clear ();
1643 all_regions.clear ();
1646 void
1647 Playlist::clear (bool with_signals)
1650 RegionLock rl (this);
1652 region_state_changed_connections.drop_connections ();
1654 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1655 pending_removes.insert (*i);
1658 regions.clear ();
1660 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1661 remove_dependents (*s);
1665 if (with_signals) {
1667 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1668 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1671 pending_removes.clear ();
1672 pending_length = false;
1673 LengthChanged ();
1674 pending_contents_change = false;
1675 ContentsChanged ();
1680 /***********************************************************************
1681 FINDING THINGS
1682 **********************************************************************/
1684 Playlist::RegionList *
1685 Playlist::regions_at (framepos_t frame)
1688 RegionLock rlock (this);
1689 return find_regions_at (frame);
1692 boost::shared_ptr<Region>
1693 Playlist::top_region_at (framepos_t frame)
1696 RegionLock rlock (this);
1697 RegionList *rlist = find_regions_at (frame);
1698 boost::shared_ptr<Region> region;
1700 if (rlist->size()) {
1701 RegionSortByLayer cmp;
1702 rlist->sort (cmp);
1703 region = rlist->back();
1706 delete rlist;
1707 return region;
1710 boost::shared_ptr<Region>
1711 Playlist::top_unmuted_region_at (framepos_t frame)
1714 RegionLock rlock (this);
1715 RegionList *rlist = find_regions_at (frame);
1717 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1719 RegionList::iterator tmp = i;
1720 ++tmp;
1722 if ((*i)->muted()) {
1723 rlist->erase (i);
1726 i = tmp;
1729 boost::shared_ptr<Region> region;
1731 if (rlist->size()) {
1732 RegionSortByLayer cmp;
1733 rlist->sort (cmp);
1734 region = rlist->back();
1737 delete rlist;
1738 return region;
1741 Playlist::RegionList*
1742 Playlist::regions_to_read (framepos_t start, framepos_t end)
1744 /* Caller must hold lock */
1746 RegionList covering;
1747 set<framepos_t> to_check;
1748 set<boost::shared_ptr<Region> > unique;
1750 to_check.insert (start);
1751 to_check.insert (end);
1753 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1755 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1757 /* find all/any regions that span start+end */
1759 switch ((*i)->coverage (start, end)) {
1760 case OverlapNone:
1761 break;
1763 case OverlapInternal:
1764 covering.push_back (*i);
1765 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1766 break;
1768 case OverlapStart:
1769 to_check.insert ((*i)->position());
1770 if ((*i)->position() != 0) {
1771 to_check.insert ((*i)->position()-1);
1773 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1774 covering.push_back (*i);
1775 break;
1777 case OverlapEnd:
1778 to_check.insert ((*i)->last_frame());
1779 to_check.insert ((*i)->last_frame()+1);
1780 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1781 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1782 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1783 covering.push_back (*i);
1784 break;
1786 case OverlapExternal:
1787 covering.push_back (*i);
1788 to_check.insert ((*i)->position());
1789 if ((*i)->position() != 0) {
1790 to_check.insert ((*i)->position()-1);
1792 to_check.insert ((*i)->last_frame());
1793 to_check.insert ((*i)->last_frame()+1);
1794 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1795 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1796 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1797 break;
1800 /* don't go too far */
1802 if ((*i)->position() > end) {
1803 break;
1807 RegionList* rlist = new RegionList;
1809 /* find all the regions that cover each position .... */
1811 if (covering.size() == 1) {
1813 rlist->push_back (covering.front());
1814 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1816 } else {
1818 RegionList here;
1819 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1821 here.clear ();
1823 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1825 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1827 if ((*x)->covers (*t)) {
1828 here.push_back (*x);
1829 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1830 (*x)->name(),
1831 (*t)));
1832 } else {
1833 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1834 (*x)->name(),
1835 (*t)));
1840 RegionSortByLayer cmp;
1841 here.sort (cmp);
1843 /* ... and get the top/transparent regions at "here" */
1845 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1847 unique.insert (*c);
1849 if ((*c)->opaque()) {
1851 /* the other regions at this position are hidden by this one */
1852 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1853 (*c)->name()));
1854 break;
1859 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1860 rlist->push_back (*s);
1863 if (rlist->size() > 1) {
1864 /* now sort by time order */
1866 RegionSortByPosition cmp;
1867 rlist->sort (cmp);
1871 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1873 return rlist;
1876 Playlist::RegionList *
1877 Playlist::find_regions_at (framepos_t frame)
1879 /* Caller must hold lock */
1881 RegionList *rlist = new RegionList;
1883 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1884 if ((*i)->covers (frame)) {
1885 rlist->push_back (*i);
1889 return rlist;
1892 Playlist::RegionList *
1893 Playlist::regions_touched (framepos_t start, framepos_t end)
1895 RegionLock rlock (this);
1896 RegionList *rlist = new RegionList;
1898 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1899 if ((*i)->coverage (start, end) != OverlapNone) {
1900 rlist->push_back (*i);
1904 return rlist;
1907 framepos_t
1908 Playlist::find_next_transient (framepos_t from, int dir)
1910 RegionLock rlock (this);
1911 AnalysisFeatureList points;
1912 AnalysisFeatureList these_points;
1914 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1915 if (dir > 0) {
1916 if ((*i)->last_frame() < from) {
1917 continue;
1919 } else {
1920 if ((*i)->first_frame() > from) {
1921 continue;
1925 (*i)->get_transients (these_points);
1927 /* add first frame, just, err, because */
1929 these_points.push_back ((*i)->first_frame());
1931 points.insert (points.end(), these_points.begin(), these_points.end());
1932 these_points.clear ();
1935 if (points.empty()) {
1936 return -1;
1939 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1940 bool reached = false;
1942 if (dir > 0) {
1943 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1944 if ((*x) >= from) {
1945 reached = true;
1948 if (reached && (*x) > from) {
1949 return *x;
1952 } else {
1953 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1954 if ((*x) <= from) {
1955 reached = true;
1958 if (reached && (*x) < from) {
1959 return *x;
1964 return -1;
1967 boost::shared_ptr<Region>
1968 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1970 RegionLock rlock (this);
1971 boost::shared_ptr<Region> ret;
1972 framepos_t closest = max_frames;
1974 bool end_iter = false;
1976 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1978 if(end_iter) break;
1980 frameoffset_t distance;
1981 boost::shared_ptr<Region> r = (*i);
1982 framepos_t pos = 0;
1984 switch (point) {
1985 case Start:
1986 pos = r->first_frame ();
1987 break;
1988 case End:
1989 pos = r->last_frame ();
1990 break;
1991 case SyncPoint:
1992 pos = r->sync_position ();
1993 // r->adjust_to_sync (r->first_frame());
1994 break;
1997 switch (dir) {
1998 case 1: /* forwards */
2000 if (pos > frame) {
2001 if ((distance = pos - frame) < closest) {
2002 closest = distance;
2003 ret = r;
2004 end_iter = true;
2008 break;
2010 default: /* backwards */
2012 if (pos < frame) {
2013 if ((distance = frame - pos) < closest) {
2014 closest = distance;
2015 ret = r;
2018 else {
2019 end_iter = true;
2022 break;
2026 return ret;
2029 framepos_t
2030 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2032 RegionLock rlock (this);
2034 framepos_t closest = max_frames;
2035 framepos_t ret = -1;
2037 if (dir > 0) {
2039 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2041 boost::shared_ptr<Region> r = (*i);
2042 frameoffset_t distance;
2044 if (r->first_frame() > frame) {
2046 distance = r->first_frame() - frame;
2048 if (distance < closest) {
2049 ret = r->first_frame();
2050 closest = distance;
2054 if (r->last_frame () > frame) {
2056 distance = r->last_frame () - frame;
2058 if (distance < closest) {
2059 ret = r->last_frame ();
2060 closest = distance;
2065 } else {
2067 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2069 boost::shared_ptr<Region> r = (*i);
2070 frameoffset_t distance;
2072 if (r->last_frame() < frame) {
2074 distance = frame - r->last_frame();
2076 if (distance < closest) {
2077 ret = r->last_frame();
2078 closest = distance;
2082 if (r->first_frame() < frame) {
2084 distance = frame - r->first_frame();
2086 if (distance < closest) {
2087 ret = r->first_frame();
2088 closest = distance;
2094 return ret;
2098 /***********************************************************************/
2103 void
2104 Playlist::mark_session_dirty ()
2106 if (!in_set_state && !holding_state ()) {
2107 _session.set_dirty();
2111 bool
2112 Playlist::set_property (const PropertyBase& prop)
2114 if (prop == Properties::regions.property_id) {
2115 const RegionListProperty::ChangeRecord& change (dynamic_cast<const RegionListProperty*>(&prop)->change());
2116 regions.update (change);
2117 return (!change.added.empty() && !change.removed.empty());
2119 return false;
2122 void
2123 Playlist::rdiff (vector<StatefulDiffCommand*>& cmds) const
2125 RegionLock rlock (const_cast<Playlist *> (this));
2127 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2128 if ((*i)->changed ()) {
2129 StatefulDiffCommand* sdc = new StatefulDiffCommand (*i);
2130 cmds.push_back (sdc);
2135 void
2136 Playlist::clear_owned_history ()
2138 RegionLock rlock (this);
2140 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2141 (*i)->clear_history ();
2145 void
2146 Playlist::update (const RegionListProperty::ChangeRecord& change)
2148 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2149 name(), change.added.size(), change.removed.size()));
2151 freeze ();
2152 /* add the added regions */
2153 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2154 add_region ((*i), (*i)->position());
2156 /* remove the removed regions */
2157 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2158 remove_region (*i);
2161 thaw ();
2164 PropertyList*
2165 Playlist::property_factory (const XMLNode& history_node) const
2167 const XMLNodeList& children (history_node.children());
2168 PropertyList* prop_list = 0;
2170 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2172 if ((*i)->name() == capitalize (regions.property_name())) {
2174 RegionListProperty* rlp = new RegionListProperty (*const_cast<Playlist*> (this));
2176 if (rlp->load_history_state (**i)) {
2177 if (!prop_list) {
2178 prop_list = new PropertyList();
2180 prop_list->add (rlp);
2181 } else {
2182 delete rlp;
2187 return prop_list;
2191 Playlist::set_state (const XMLNode& node, int version)
2193 XMLNode *child;
2194 XMLNodeList nlist;
2195 XMLNodeConstIterator niter;
2196 XMLPropertyList plist;
2197 XMLPropertyConstIterator piter;
2198 XMLProperty *prop;
2199 boost::shared_ptr<Region> region;
2200 string region_name;
2202 in_set_state++;
2204 if (node.name() != "Playlist") {
2205 in_set_state--;
2206 return -1;
2209 freeze ();
2211 plist = node.properties();
2213 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2215 prop = *piter;
2217 if (prop->name() == X_("name")) {
2218 _name = prop->value();
2219 _set_sort_id ();
2220 } else if (prop->name() == X_("id")) {
2221 _id = prop->value();
2222 } else if (prop->name() == X_("orig_diskstream_id")) {
2223 _orig_diskstream_id = prop->value ();
2224 } else if (prop->name() == X_("frozen")) {
2225 _frozen = string_is_affirmative (prop->value());
2229 clear (true);
2231 nlist = node.children();
2233 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2235 child = *niter;
2237 if (child->name() == "Region") {
2239 if ((prop = child->property ("id")) == 0) {
2240 error << _("region state node has no ID, ignored") << endmsg;
2241 continue;
2244 ID id = prop->value ();
2246 if ((region = region_by_id (id))) {
2248 region->suspend_property_changes ();
2250 if (region->set_state (*child, version)) {
2251 region->resume_property_changes ();
2252 continue;
2255 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2256 region->suspend_property_changes ();
2257 } else {
2258 error << _("Playlist: cannot create region from XML") << endmsg;
2259 continue;
2263 add_region (region, region->position(), 1.0);
2265 // So that layer_op ordering doesn't get screwed up
2266 region->set_last_layer_op( region->layer());
2267 region->resume_property_changes ();
2271 /* update dependents, which was not done during add_region_internal
2272 due to in_set_state being true
2275 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2276 check_dependents (*r, false);
2279 thaw ();
2280 notify_contents_changed ();
2282 in_set_state--;
2283 first_set_state = false;
2284 return 0;
2287 XMLNode&
2288 Playlist::get_state()
2290 return state (true);
2293 XMLNode&
2294 Playlist::get_template()
2296 return state (false);
2299 /** @param full_state true to include regions in the returned state, otherwise false.
2301 XMLNode&
2302 Playlist::state (bool full_state)
2304 XMLNode *node = new XMLNode (X_("Playlist"));
2305 char buf[64] = "";
2307 node->add_property (X_("id"), id().to_s());
2308 node->add_property (X_("name"), _name);
2309 node->add_property (X_("type"), _type.to_string());
2311 _orig_diskstream_id.print (buf, sizeof (buf));
2312 node->add_property (X_("orig_diskstream_id"), buf);
2313 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2315 if (full_state) {
2316 RegionLock rlock (this, false);
2318 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2319 node->add_child_nocopy ((*i)->get_state());
2323 if (_extra_xml) {
2324 node->add_child_copy (*_extra_xml);
2327 return *node;
2330 bool
2331 Playlist::empty() const
2333 RegionLock rlock (const_cast<Playlist *>(this), false);
2334 return regions.empty();
2337 uint32_t
2338 Playlist::n_regions() const
2340 RegionLock rlock (const_cast<Playlist *>(this), false);
2341 return regions.size();
2344 pair<framecnt_t, framecnt_t>
2345 Playlist::get_extent () const
2347 RegionLock rlock (const_cast<Playlist *>(this), false);
2348 return _get_extent ();
2351 pair<framecnt_t, framecnt_t>
2352 Playlist::_get_extent () const
2354 pair<framecnt_t, framecnt_t> ext (max_frames, 0);
2356 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2357 pair<framecnt_t, framecnt_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2358 if (e.first < ext.first) {
2359 ext.first = e.first;
2361 if (e.second > ext.second) {
2362 ext.second = e.second;
2366 return ext;
2369 string
2370 Playlist::bump_name (string name, Session &session)
2372 string newname = name;
2374 do {
2375 newname = bump_name_once (newname, '.');
2376 } while (session.playlists->by_name (newname)!=NULL);
2378 return newname;
2382 layer_t
2383 Playlist::top_layer() const
2385 RegionLock rlock (const_cast<Playlist *> (this));
2386 layer_t top = 0;
2388 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2389 top = max (top, (*i)->layer());
2391 return top;
2394 void
2395 Playlist::set_edit_mode (EditMode mode)
2397 _edit_mode = mode;
2400 /********************
2401 * Region Layering
2402 ********************/
2404 void
2405 Playlist::relayer ()
2407 /* never compute layers when changing state for undo/redo or setting from XML */
2409 if (in_update || in_set_state) {
2410 return;
2413 bool changed = false;
2415 /* Build up a new list of regions on each layer, stored in a set of lists
2416 each of which represent some period of time on some layer. The idea
2417 is to avoid having to search the entire region list to establish whether
2418 each region overlaps another */
2420 /* how many pieces to divide this playlist's time up into */
2421 int const divisions = 512;
2423 /* find the start and end positions of the regions on this playlist */
2424 framepos_t start = INT64_MAX;
2425 framepos_t end = 0;
2426 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2427 start = min (start, (*i)->position());
2428 end = max (end, (*i)->position() + (*i)->length());
2431 /* hence the size of each time division */
2432 double const division_size = (end - start) / double (divisions);
2434 vector<vector<RegionList> > layers;
2435 layers.push_back (vector<RegionList> (divisions));
2437 /* we want to go through regions from desired lowest to desired highest layer,
2438 which depends on the layer model
2441 RegionList copy = regions.rlist();
2443 /* sort according to the model and the layering mode that we're in */
2445 if (_explicit_relayering) {
2447 copy.sort (RegionSortByLayerWithPending ());
2449 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2451 copy.sort (RegionSortByLastLayerOp ());
2456 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2458 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2459 (*i)->set_pending_explicit_relayer (false);
2461 /* find the time divisions that this region covers; if there are no regions on the list,
2462 division_size will equal 0 and in this case we'll just say that
2463 start_division = end_division = 0.
2465 int start_division = 0;
2466 int end_division = 0;
2468 if (division_size > 0) {
2469 start_division = floor ( ((*i)->position() - start) / division_size);
2470 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2471 if (end_division == divisions) {
2472 end_division--;
2476 assert (divisions == 0 || end_division < divisions);
2478 /* find the lowest layer that this region can go on */
2479 size_t j = layers.size();
2480 while (j > 0) {
2481 /* try layer j - 1; it can go on if it overlaps no other region
2482 that is already on that layer
2485 bool overlap = false;
2486 for (int k = start_division; k <= end_division; ++k) {
2487 RegionList::iterator l = layers[j-1][k].begin ();
2488 while (l != layers[j-1][k].end()) {
2489 if ((*l)->overlap_equivalent (*i)) {
2490 overlap = true;
2491 break;
2493 l++;
2496 if (overlap) {
2497 break;
2501 if (overlap) {
2502 /* overlap, so we must use layer j */
2503 break;
2506 --j;
2509 if (j == layers.size()) {
2510 /* we need a new layer for this region */
2511 layers.push_back (vector<RegionList> (divisions));
2514 /* put a reference to this region in each of the divisions that it exists in */
2515 for (int k = start_division; k <= end_division; ++k) {
2516 layers[j][k].push_back (*i);
2519 if ((*i)->layer() != j) {
2520 changed = true;
2523 (*i)->set_layer (j);
2526 if (changed) {
2527 notify_layering_changed ();
2531 /* XXX these layer functions are all deprecated */
2533 void
2534 Playlist::raise_region (boost::shared_ptr<Region> region)
2536 uint32_t rsz = regions.size();
2537 layer_t target = region->layer() + 1U;
2539 if (target >= rsz) {
2540 /* its already at the effective top */
2541 return;
2544 move_region_to_layer (target, region, 1);
2547 void
2548 Playlist::lower_region (boost::shared_ptr<Region> region)
2550 if (region->layer() == 0) {
2551 /* its already at the bottom */
2552 return;
2555 layer_t target = region->layer() - 1U;
2557 move_region_to_layer (target, region, -1);
2560 void
2561 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2563 /* does nothing useful if layering mode is later=higher */
2564 switch (_session.config.get_layer_model()) {
2565 case LaterHigher:
2566 return;
2567 default:
2568 break;
2571 layer_t top = regions.size() - 1;
2573 if (region->layer() >= top) {
2574 /* already on the top */
2575 return;
2578 move_region_to_layer (top, region, 1);
2579 /* mark the region's last_layer_op as now, so that it remains on top when
2580 doing future relayers (until something else takes over)
2582 timestamp_layer_op (region);
2585 void
2586 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2588 /* does nothing useful if layering mode is later=higher */
2589 switch (_session.config.get_layer_model()) {
2590 case LaterHigher:
2591 return;
2592 default:
2593 break;
2596 if (region->layer() == 0) {
2597 /* already on the bottom */
2598 return;
2601 move_region_to_layer (0, region, -1);
2602 /* force region's last layer op to zero so that it stays at the bottom
2603 when doing future relayers
2605 region->set_last_layer_op (0);
2609 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2611 RegionList::iterator i;
2612 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2613 list<LayerInfo> layerinfo;
2616 RegionLock rlock (const_cast<Playlist *> (this));
2618 for (i = regions.begin(); i != regions.end(); ++i) {
2620 if (region == *i) {
2621 continue;
2624 layer_t dest;
2626 if (dir > 0) {
2628 /* region is moving up, move all regions on intermediate layers
2629 down 1
2632 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2633 dest = (*i)->layer() - 1;
2634 } else {
2635 /* not affected */
2636 continue;
2638 } else {
2640 /* region is moving down, move all regions on intermediate layers
2641 up 1
2644 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2645 dest = (*i)->layer() + 1;
2646 } else {
2647 /* not affected */
2648 continue;
2652 LayerInfo newpair;
2654 newpair.first = *i;
2655 newpair.second = dest;
2657 layerinfo.push_back (newpair);
2661 /* now reset the layers without holding the region lock */
2663 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2664 x->first->set_layer (x->second);
2667 region->set_layer (target_layer);
2669 #if 0
2670 /* now check all dependents */
2672 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2673 check_dependents (x->first, false);
2676 check_dependents (region, false);
2677 #endif
2679 return 0;
2682 void
2683 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2685 RegionList::iterator i;
2686 bool moved = false;
2688 _nudging = true;
2691 RegionLock rlock (const_cast<Playlist *> (this));
2693 for (i = regions.begin(); i != regions.end(); ++i) {
2695 if ((*i)->position() >= start) {
2697 framepos_t new_pos;
2699 if (forwards) {
2701 if ((*i)->last_frame() > max_frames - distance) {
2702 new_pos = max_frames - (*i)->length();
2703 } else {
2704 new_pos = (*i)->position() + distance;
2707 } else {
2709 if ((*i)->position() > distance) {
2710 new_pos = (*i)->position() - distance;
2711 } else {
2712 new_pos = 0;
2716 (*i)->set_position (new_pos, this);
2717 moved = true;
2722 if (moved) {
2723 _nudging = false;
2724 notify_length_changed ();
2729 boost::shared_ptr<Region>
2730 Playlist::find_region (const ID& id) const
2732 RegionLock rlock (const_cast<Playlist*> (this));
2734 /* searches all regions currently in use by the playlist */
2736 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2737 if ((*i)->id() == id) {
2738 return *i;
2742 return boost::shared_ptr<Region> ();
2745 boost::shared_ptr<Region>
2746 Playlist::region_by_id (const ID& id)
2748 /* searches all regions ever added to this playlist */
2750 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2751 if ((*i)->id() == id) {
2752 return *i;
2755 return boost::shared_ptr<Region> ();
2758 void
2759 Playlist::dump () const
2761 boost::shared_ptr<Region> r;
2763 cerr << "Playlist \"" << _name << "\" " << endl
2764 << regions.size() << " regions "
2765 << endl;
2767 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2768 r = *i;
2769 cerr << " " << r->name() << " ["
2770 << r->start() << "+" << r->length()
2771 << "] at "
2772 << r->position()
2773 << " on layer "
2774 << r->layer ()
2775 << endl;
2779 void
2780 Playlist::set_frozen (bool yn)
2782 _frozen = yn;
2785 void
2786 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2788 region->set_last_layer_op (++layer_op_counter);
2792 void
2793 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2795 bool moved = false;
2797 if (region->locked()) {
2798 return;
2801 _shuffling = true;
2804 RegionLock rlock (const_cast<Playlist*> (this));
2807 if (dir > 0) {
2809 RegionList::iterator next;
2811 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2812 if ((*i) == region) {
2813 next = i;
2814 ++next;
2816 if (next != regions.end()) {
2818 if ((*next)->locked()) {
2819 break;
2822 framepos_t new_pos;
2824 if ((*next)->position() != region->last_frame() + 1) {
2825 /* they didn't used to touch, so after shuffle,
2826 just have them swap positions.
2828 new_pos = (*next)->position();
2829 } else {
2830 /* they used to touch, so after shuffle,
2831 make sure they still do. put the earlier
2832 region where the later one will end after
2833 it is moved.
2835 new_pos = region->position() + (*next)->length();
2838 (*next)->set_position (region->position(), this);
2839 region->set_position (new_pos, this);
2841 /* avoid a full sort */
2843 regions.erase (i); // removes the region from the list */
2844 next++;
2845 regions.insert (next, region); // adds it back after next
2847 moved = true;
2849 break;
2852 } else {
2854 RegionList::iterator prev = regions.end();
2856 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2857 if ((*i) == region) {
2859 if (prev != regions.end()) {
2861 if ((*prev)->locked()) {
2862 break;
2865 framepos_t new_pos;
2866 if (region->position() != (*prev)->last_frame() + 1) {
2867 /* they didn't used to touch, so after shuffle,
2868 just have them swap positions.
2870 new_pos = region->position();
2871 } else {
2872 /* they used to touch, so after shuffle,
2873 make sure they still do. put the earlier
2874 one where the later one will end after
2876 new_pos = (*prev)->position() + region->length();
2879 region->set_position ((*prev)->position(), this);
2880 (*prev)->set_position (new_pos, this);
2882 /* avoid a full sort */
2884 regions.erase (i); // remove region
2885 regions.insert (prev, region); // insert region before prev
2887 moved = true;
2890 break;
2896 _shuffling = false;
2898 if (moved) {
2900 relayer ();
2901 check_dependents (region, false);
2903 notify_contents_changed();
2908 bool
2909 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2911 RegionLock rlock (const_cast<Playlist*> (this));
2913 if (regions.size() > 1) {
2914 return true;
2917 return false;
2920 void
2921 Playlist::update_after_tempo_map_change ()
2923 RegionLock rlock (const_cast<Playlist*> (this));
2924 RegionList copy (regions.rlist());
2926 freeze ();
2928 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2929 (*i)->update_position_after_tempo_map_change ();
2932 thaw ();
2935 void
2936 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2938 RegionLock rl (this, false);
2939 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2940 s (*i);
2944 void
2945 Playlist::set_explicit_relayering (bool e)
2947 if (e == false && _explicit_relayering == true) {
2949 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2950 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2951 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2952 at this point would keep regions on the same layers.
2954 From then on in, it's just you and your towel.
2957 RegionLock rl (this);
2958 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2959 (*i)->set_last_layer_op ((*i)->layer ());
2963 _explicit_relayering = e;
2967 bool
2968 Playlist::has_region_at (framepos_t const p) const
2970 RegionLock (const_cast<Playlist *> (this));
2972 RegionList::const_iterator i = regions.begin ();
2973 while (i != regions.end() && !(*i)->covers (p)) {
2974 ++i;
2977 return (i != regions.end());
2980 /** Remove any region that uses a given source */
2981 void
2982 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2984 RegionLock rl (this);
2986 RegionList::iterator i = regions.begin();
2987 while (i != regions.end()) {
2988 RegionList::iterator j = i;
2989 ++j;
2991 if ((*i)->uses_source (s)) {
2992 remove_region_internal (*i);
2995 i = j;