use region sync points correctly for grid and PH motion (i hope)
[ardour2.git] / libs / ardour / playlist.cc
blob9960074e9a7c6f7aa733daccf5d2c2b66c05f776
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 <set>
21 #include <fstream>
22 #include <algorithm>
23 #include <unistd.h>
24 #include <cerrno>
25 #include <string>
26 #include <climits>
28 #include <sigc++/bind.h>
30 #include <pbd/failed_constructor.h>
31 #include <pbd/stl_delete.h>
32 #include <pbd/xml++.h>
33 #include <pbd/stacktrace.h>
35 #include <ardour/playlist.h>
36 #include <ardour/session.h>
37 #include <ardour/region.h>
38 #include <ardour/region_factory.h>
39 #include <ardour/playlist_factory.h>
40 #include <ardour/transient_detector.h>
42 #include "i18n.h"
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
48 struct ShowMeTheList {
49 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
50 ~ShowMeTheList () {
51 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
53 boost::shared_ptr<Playlist> playlist;
54 string name;
57 struct RegionSortByLayer {
58 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
59 return a->layer() < b->layer();
63 struct RegionSortByPosition {
64 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
65 return a->position() < b->position();
69 struct RegionSortByLastLayerOp {
70 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
71 return a->last_layer_op() < b->last_layer_op();
75 Playlist::Playlist (Session& sess, string nom, bool hide)
76 : _session (sess)
78 init (hide);
79 first_set_state = false;
80 _name = nom;
84 Playlist::Playlist (Session& sess, const XMLNode& node, bool hide)
85 : _session (sess)
87 init (hide);
88 _name = "unnamed"; /* reset by set_state */
90 /* set state called by derived class */
93 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
94 : _name (namestr), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
96 init (hide);
98 RegionList tmp;
99 other->copy_regions (tmp);
101 in_set_state++;
103 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
104 add_region_internal( (*x), (*x)->position());
107 in_set_state--;
109 _splicing = other->_splicing;
110 _nudging = other->_nudging;
111 _edit_mode = other->_edit_mode;
113 in_set_state = 0;
114 first_set_state = false;
115 in_flush = false;
116 in_partition = false;
117 subcnt = 0;
118 _read_data_count = 0;
119 _frozen = other->_frozen;
121 layer_op_counter = other->layer_op_counter;
122 freeze_length = other->freeze_length;
125 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
126 : _name (str), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
128 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
130 nframes_t end = start + cnt - 1;
132 init (hide);
134 in_set_state++;
136 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
138 boost::shared_ptr<Region> region;
139 boost::shared_ptr<Region> new_region;
140 nframes_t offset = 0;
141 nframes_t position = 0;
142 nframes_t len = 0;
143 string new_name;
144 OverlapType overlap;
146 region = *i;
148 overlap = region->coverage (start, end);
150 switch (overlap) {
151 case OverlapNone:
152 continue;
154 case OverlapInternal:
155 offset = start - region->position();
156 position = 0;
157 len = cnt;
158 break;
160 case OverlapStart:
161 offset = 0;
162 position = region->position() - start;
163 len = end - region->position();
164 break;
166 case OverlapEnd:
167 offset = start - region->position();
168 position = 0;
169 len = region->length() - offset;
170 break;
172 case OverlapExternal:
173 offset = 0;
174 position = region->position() - start;
175 len = region->length();
176 break;
179 _session.region_name (new_name, region->name(), false);
181 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
183 add_region_internal (new_region, position);
186 in_set_state--;
187 first_set_state = false;
189 /* this constructor does NOT notify others (session) */
192 void
193 Playlist::use ()
195 ++_refcnt;
196 InUse (true); /* EMIT SIGNAL */
199 void
200 Playlist::release ()
202 if (_refcnt > 0) {
203 _refcnt--;
206 if (_refcnt == 0) {
207 InUse (false); /* EMIT SIGNAL */
211 void
212 Playlist::copy_regions (RegionList& newlist) const
214 RegionLock rlock (const_cast<Playlist *> (this));
216 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
217 newlist.push_back (RegionFactory::RegionFactory::create (*i));
221 void
222 Playlist::init (bool hide)
224 g_atomic_int_set (&block_notifications, 0);
225 g_atomic_int_set (&ignore_state_changes, 0);
226 pending_modified = false;
227 pending_length = false;
228 first_set_state = true;
229 _refcnt = 0;
230 _hidden = hide;
231 _splicing = false;
232 _shuffling = false;
233 _nudging = false;
234 in_set_state = 0;
235 _edit_mode = Config->get_edit_mode();
236 in_flush = false;
237 in_partition = false;
238 subcnt = 0;
239 _read_data_count = 0;
240 _frozen = false;
241 layer_op_counter = 0;
242 freeze_length = 0;
244 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
247 Playlist::Playlist (const Playlist& pl)
248 : _session (pl._session)
250 fatal << _("playlist const copy constructor called") << endmsg;
253 Playlist::Playlist (Playlist& pl)
254 : _session (pl._session)
256 fatal << _("playlist non-const copy constructor called") << endmsg;
259 Playlist::~Playlist ()
262 RegionLock rl (this);
264 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
265 (*i)->set_playlist (boost::shared_ptr<Playlist>());
269 /* GoingAway must be emitted by derived classes */
272 void
273 Playlist::set_name (string str)
275 /* in a typical situation, a playlist is being used
276 by one diskstream and also is referenced by the
277 Session. if there are more references than that,
278 then don't change the name.
281 if (_refcnt > 2) {
282 return;
285 if (str == _name) {
286 return;
289 string name = str;
291 while (_session.playlist_by_name(name) != 0) {
292 name = bump_name_once(name);
295 _name = name;
296 NameChanged(); /* EMIT SIGNAL */
299 /***********************************************************************
300 CHANGE NOTIFICATION HANDLING
302 Notifications must be delayed till the region_lock is released. This
303 is necessary because handlers for the signals may need to acquire
304 the lock (e.g. to read from the playlist).
305 ***********************************************************************/
307 void
308 Playlist::freeze ()
310 delay_notifications ();
311 g_atomic_int_inc (&ignore_state_changes);
314 void
315 Playlist::thaw ()
317 g_atomic_int_dec_and_test (&ignore_state_changes);
318 release_notifications ();
322 void
323 Playlist::delay_notifications ()
325 g_atomic_int_inc (&block_notifications);
326 freeze_length = _get_maximum_extent();
329 void
330 Playlist::release_notifications ()
332 if (g_atomic_int_dec_and_test (&block_notifications)) {
333 flush_notifications ();
337 void
338 Playlist::notify_modified ()
340 if (holding_state ()) {
341 pending_modified = true;
342 } else {
343 pending_modified = false;
344 Modified(); /* EMIT SIGNAL */
348 void
349 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
351 if (holding_state ()) {
352 pending_removes.insert (r);
353 pending_modified = true;
354 pending_length = true;
355 } else {
356 /* this might not be true, but we have to act
357 as though it could be.
359 LengthChanged (); /* EMIT SIGNAL */
360 Modified (); /* EMIT SIGNAL */
364 void
365 Playlist::notify_region_added (boost::shared_ptr<Region> r)
367 /* the length change might not be true, but we have to act
368 as though it could be.
371 if (holding_state()) {
372 pending_adds.insert (r);
373 pending_modified = true;
374 pending_length = true;
375 } else {
376 LengthChanged (); /* EMIT SIGNAL */
377 Modified (); /* EMIT SIGNAL */
381 void
382 Playlist::notify_length_changed ()
384 if (holding_state ()) {
385 pending_length = true;
386 } else {
387 LengthChanged(); /* EMIT SIGNAL */
388 Modified (); /* EMIT SIGNAL */
392 void
393 Playlist::flush_notifications ()
395 set<boost::shared_ptr<Region> > dependent_checks_needed;
396 set<boost::shared_ptr<Region> >::iterator s;
397 uint32_t n = 0;
399 if (in_flush) {
400 return;
403 in_flush = true;
405 /* we have no idea what order the regions ended up in pending
406 bounds (it could be based on selection order, for example).
407 so, to preserve layering in the "most recently moved is higher"
408 model, sort them by existing layer, then timestamp them.
411 // RegionSortByLayer cmp;
412 // pending_bounds.sort (cmp);
414 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
415 if (Config->get_layer_model() == MoveAddHigher) {
416 timestamp_layer_op (*r);
418 pending_length = true;
419 dependent_checks_needed.insert (*r);
420 n++;
423 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
424 dependent_checks_needed.insert (*s);
425 n++;
428 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
429 remove_dependents (*s);
430 n++;
433 if ((freeze_length != _get_maximum_extent()) || pending_length) {
434 pending_length = 0;
435 LengthChanged(); /* EMIT SIGNAL */
436 n++;
439 if (n || pending_modified) {
440 if (!in_set_state) {
441 relayer ();
443 pending_modified = false;
444 Modified (); /* EMIT SIGNAL */
447 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
448 check_dependents (*s, false);
451 pending_adds.clear ();
452 pending_removes.clear ();
453 pending_bounds.clear ();
455 in_flush = false;
458 /*************************************************************
459 PLAYLIST OPERATIONS
460 *************************************************************/
462 void
463 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
465 RegionLock rlock (this);
467 times = fabs (times);
469 int itimes = (int) floor (times);
471 nframes_t pos = position;
473 if (itimes >= 1) {
474 add_region_internal (region, pos);
475 pos += region->length();
476 --itimes;
480 /* note that itimes can be zero if we being asked to just
481 insert a single fraction of the region.
484 for (int i = 0; i < itimes; ++i) {
485 boost::shared_ptr<Region> copy = RegionFactory::create (region);
486 add_region_internal (copy, pos);
487 pos += region->length();
490 nframes_t length = 0;
492 if (floor (times) != times) {
493 length = (nframes_t) floor (region->length() * (times - floor (times)));
494 string name;
495 _session.region_name (name, region->name(), false);
496 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
497 add_region_internal (sub, pos);
501 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
504 void
505 Playlist::set_region_ownership ()
507 RegionLock rl (this);
508 RegionList::iterator i;
509 boost::weak_ptr<Playlist> pl (shared_from_this());
511 for (i = regions.begin(); i != regions.end(); ++i) {
512 (*i)->set_playlist (pl);
516 void
517 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
519 RegionSortByPosition cmp;
520 nframes_t old_length = 0;
522 if (!holding_state()) {
523 old_length = _get_maximum_extent();
526 if (!first_set_state) {
527 boost::shared_ptr<Playlist> foo (shared_from_this());
528 region->set_playlist (boost::weak_ptr<Playlist>(foo));
531 region->set_position (position, this);
533 timestamp_layer_op (region);
535 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
536 all_regions.insert (region);
538 possibly_splice_unlocked (position, region->length(), region);
540 if (!holding_state () && !in_set_state) {
541 /* layers get assigned from XML state */
542 relayer ();
545 /* we need to notify the existence of new region before checking dependents. Ick. */
547 notify_region_added (region);
549 if (!holding_state ()) {
550 check_dependents (region, false);
551 if (old_length != _get_maximum_extent()) {
552 notify_length_changed ();
556 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
557 boost::weak_ptr<Region> (region)));
560 void
561 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
563 RegionLock rlock (this);
565 bool old_sp = _splicing;
566 _splicing = true;
568 remove_region_internal (old);
569 add_region_internal (newr, pos);
571 _splicing = old_sp;
573 possibly_splice_unlocked (pos, (nframes64_t) old->length() - (nframes64_t) newr->length());
576 void
577 Playlist::remove_region (boost::shared_ptr<Region> region)
579 RegionLock rlock (this);
580 remove_region_internal (region);
584 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
586 RegionList::iterator i;
587 nframes_t old_length = 0;
589 if (!holding_state()) {
590 old_length = _get_maximum_extent();
593 if (!in_set_state) {
594 /* unset playlist */
595 region->set_playlist (boost::weak_ptr<Playlist>());
598 for (i = regions.begin(); i != regions.end(); ++i) {
599 if (*i == region) {
601 nframes_t pos = (*i)->position();
602 nframes64_t distance = (*i)->length();
604 regions.erase (i);
606 possibly_splice_unlocked (pos, -distance);
608 if (!holding_state ()) {
609 relayer ();
610 remove_dependents (region);
612 if (old_length != _get_maximum_extent()) {
613 notify_length_changed ();
617 notify_region_removed (region);
618 return 0;
624 return -1;
627 void
628 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
630 if (Config->get_use_overlap_equivalency()) {
631 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
632 if ((*i)->overlap_equivalent (other)) {
633 results.push_back ((*i));
636 } else {
637 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
638 if ((*i)->equivalent (other)) {
639 results.push_back ((*i));
645 void
646 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
648 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
650 if ((*i) && (*i)->region_list_equivalent (other)) {
651 results.push_back (*i);
656 void
657 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
659 RegionList thawlist;
661 partition_internal (start, end, false, thawlist);
663 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
664 (*i)->thaw ("separation");
668 void
669 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
671 RegionList new_regions;
674 RegionLock rlock (this);
675 boost::shared_ptr<Region> region;
676 boost::shared_ptr<Region> current;
677 string new_name;
678 RegionList::iterator tmp;
679 OverlapType overlap;
680 nframes_t pos1, pos2, pos3, pos4;
682 in_partition = true;
684 /* need to work from a copy, because otherwise the regions we add during the process
685 get operated on as well.
688 RegionList copy = regions;
690 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
692 tmp = i;
693 ++tmp;
695 current = *i;
697 if (current->first_frame() >= start && current->last_frame() < end) {
698 if (cutting) {
699 remove_region_internal (current);
701 continue;
704 /* coverage will return OverlapStart if the start coincides
705 with the end point. we do not partition such a region,
706 so catch this special case.
709 if (current->first_frame() >= end) {
710 continue;
713 if ((overlap = current->coverage (start, end)) == OverlapNone) {
714 continue;
717 pos1 = current->position();
718 pos2 = start;
719 pos3 = end;
720 pos4 = current->last_frame();
722 if (overlap == OverlapInternal) {
724 /* split: we need 3 new regions, the front, middle and end.
725 cut: we need 2 regions, the front and end.
729 start end
730 ---------------*************************------------
731 P1 P2 P3 P4
732 SPLIT:
733 ---------------*****++++++++++++++++====------------
735 ---------------*****----------------====------------
739 if (!cutting) {
741 /* "middle" ++++++ */
743 _session.region_name (new_name, current->name(), false);
744 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
745 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
746 add_region_internal (region, start);
747 new_regions.push_back (region);
750 /* "end" ====== */
752 _session.region_name (new_name, current->name(), false);
753 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
754 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
756 add_region_internal (region, end);
757 new_regions.push_back (region);
759 /* "front" ***** */
761 current->freeze ();
762 thawlist.push_back (current);
763 current->trim_end (pos2, this);
765 } else if (overlap == OverlapEnd) {
768 start end
769 ---------------*************************------------
770 P1 P2 P4 P3
771 SPLIT:
772 ---------------**************+++++++++++------------
773 CUT:
774 ---------------**************-----------------------
777 if (!cutting) {
779 /* end +++++ */
781 _session.region_name (new_name, current->name(), false);
782 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
783 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
784 add_region_internal (region, start);
785 new_regions.push_back (region);
788 /* front ****** */
790 current->freeze ();
791 thawlist.push_back (current);
792 current->trim_end (pos2, this);
794 } else if (overlap == OverlapStart) {
796 /* split: we need 2 regions: the front and the end.
797 cut: just trim current to skip the cut area
801 start end
802 ---------------*************************------------
803 P2 P1 P3 P4
805 SPLIT:
806 ---------------****+++++++++++++++++++++------------
807 CUT:
808 -------------------*********************------------
812 if (!cutting) {
814 /* front **** */
815 _session.region_name (new_name, current->name(), false);
816 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
817 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
818 add_region_internal (region, pos1);
819 new_regions.push_back (region);
822 /* end */
824 current->freeze ();
825 thawlist.push_back (current);
826 current->trim_front (pos3, this);
828 } else if (overlap == OverlapExternal) {
830 /* split: no split required.
831 cut: remove the region.
835 start end
836 ---------------*************************------------
837 P2 P1 P3 P4
839 SPLIT:
840 ---------------*************************------------
841 CUT:
842 ----------------------------------------------------
846 if (cutting) {
847 remove_region_internal (current);
849 new_regions.push_back (current);
853 in_partition = false;
856 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
857 check_dependents (*i, false);
861 boost::shared_ptr<Playlist>
862 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
864 boost::shared_ptr<Playlist> ret;
865 boost::shared_ptr<Playlist> pl;
866 nframes_t start;
868 if (ranges.empty()) {
869 return boost::shared_ptr<Playlist>();
872 start = ranges.front().start;
874 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
876 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
878 if (i == ranges.begin()) {
879 ret = pl;
880 } else {
882 /* paste the next section into the nascent playlist,
883 offset to reflect the start of the first range we
884 chopped.
887 ret->paste (pl, (*i).start - start, 1.0f);
891 return ret;
894 boost::shared_ptr<Playlist>
895 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
897 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
898 return cut_copy (pmf, ranges, result_is_hidden);
901 boost::shared_ptr<Playlist>
902 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
904 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
905 return cut_copy (pmf, ranges, result_is_hidden);
908 boost::shared_ptr<Playlist>
909 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
911 boost::shared_ptr<Playlist> the_copy;
912 RegionList thawlist;
913 char buf[32];
915 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
916 string new_name = _name;
917 new_name += '.';
918 new_name += buf;
920 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
921 return boost::shared_ptr<Playlist>();
924 partition_internal (start, start+cnt-1, true, thawlist);
926 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
927 (*i)->thaw ("playlist cut");
930 return the_copy;
933 boost::shared_ptr<Playlist>
934 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
936 char buf[32];
938 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
939 string new_name = _name;
940 new_name += '.';
941 new_name += buf;
943 cnt = min (_get_maximum_extent() - start, cnt);
944 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
948 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
950 times = fabs (times);
951 nframes_t old_length;
954 RegionLock rl1 (this);
955 RegionLock rl2 (other.get());
957 old_length = _get_maximum_extent();
959 int itimes = (int) floor (times);
960 nframes_t pos = position;
961 nframes_t shift = other->_get_maximum_extent();
962 layer_t top_layer = regions.size();
964 while (itimes--) {
965 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
966 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
968 /* put these new regions on top of all existing ones, but preserve
969 the ordering they had in the original playlist.
972 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
973 add_region_internal (copy_of_region, copy_of_region->position() + pos);
975 pos += shift;
979 /* XXX shall we handle fractional cases at some point? */
981 if (old_length != _get_maximum_extent()) {
982 notify_length_changed ();
988 return 0;
992 void
993 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
995 times = fabs (times);
997 RegionLock rl (this);
998 int itimes = (int) floor (times);
999 nframes_t pos = position;
1001 while (itimes--) {
1002 boost::shared_ptr<Region> copy = RegionFactory::create (region);
1003 add_region_internal (copy, pos);
1004 pos += region->length();
1007 if (floor (times) != times) {
1008 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
1009 string name;
1010 _session.region_name (name, region->name(), false);
1011 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
1012 add_region_internal (sub, pos);
1016 void
1017 Playlist::shift (nframes64_t at, nframes64_t distance, bool move_intersected, bool ignore_music_glue)
1019 RegionLock rlock (this);
1020 RegionList copy (regions);
1021 RegionList fixup;
1023 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1025 if ((*r)->last_frame() < at) {
1026 /* too early */
1027 continue;
1030 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1031 /* intersected region */
1032 if (!move_intersected) {
1033 continue;
1037 /* do not move regions glued to music time - that
1038 has to be done separately.
1041 if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) {
1042 fixup.push_back (*r);
1043 continue;
1046 (*r)->set_position ((*r)->position() + distance, this);
1049 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1050 (*r)->recompute_position_from_lock_style ();
1054 void
1055 Playlist::split (nframes64_t at)
1057 RegionLock rlock (this);
1058 RegionList copy (regions);
1060 /* use a copy since this operation can modify the region list
1063 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1064 _split_region (*r, at);
1068 void
1069 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
1071 RegionLock rl (this);
1072 _split_region (region, playlist_position);
1075 void
1076 Playlist::_split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
1078 if (!region->covers (playlist_position)) {
1079 return;
1082 if (region->position() == playlist_position ||
1083 region->last_frame() == playlist_position) {
1084 return;
1087 boost::shared_ptr<Region> left;
1088 boost::shared_ptr<Region> right;
1089 nframes_t before;
1090 nframes_t after;
1091 string before_name;
1092 string after_name;
1094 /* split doesn't change anything about length, so don't try to splice */
1096 bool old_sp = _splicing;
1097 _splicing = true;
1099 before = playlist_position - region->position();
1100 after = region->length() - before;
1102 _session.region_name (before_name, region->name(), false);
1103 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1105 _session.region_name (after_name, region->name(), false);
1106 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1108 add_region_internal (left, region->position());
1109 add_region_internal (right, region->position() + before);
1111 uint64_t orig_layer_op = region->last_layer_op();
1112 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1113 if ((*i)->last_layer_op() > orig_layer_op) {
1114 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1118 left->set_last_layer_op ( orig_layer_op );
1119 right->set_last_layer_op ( orig_layer_op + 1);
1121 layer_op_counter++;
1123 finalize_split_region (region, left, right);
1125 remove_region_internal (region);
1127 _splicing = old_sp;
1130 void
1131 Playlist::possibly_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1133 if (_splicing || in_set_state) {
1134 /* don't respond to splicing moves or state setting */
1135 return;
1138 if (_edit_mode == Splice) {
1139 splice_locked (at, distance, exclude);
1143 void
1144 Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1146 if (_splicing || in_set_state) {
1147 /* don't respond to splicing moves or state setting */
1148 return;
1151 if (_edit_mode == Splice) {
1152 splice_unlocked (at, distance, exclude);
1156 void
1157 Playlist::splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1160 RegionLock rl (this);
1161 core_splice (at, distance, exclude);
1165 void
1166 Playlist::splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1168 core_splice (at, distance, exclude);
1171 void
1172 Playlist::core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1174 _splicing = true;
1176 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1178 if (exclude && (*i) == exclude) {
1179 continue;
1182 if ((*i)->position() >= at) {
1183 nframes64_t new_pos = (*i)->position() + distance;
1184 if (new_pos < 0) {
1185 new_pos = 0;
1186 } else if (new_pos >= max_frames - (*i)->length()) {
1187 new_pos = max_frames - (*i)->length();
1190 (*i)->set_position (new_pos, this);
1194 _splicing = false;
1196 notify_length_changed ();
1199 void
1200 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1202 if (in_set_state || _splicing || _nudging || _shuffling) {
1203 return;
1206 if (what_changed & ARDOUR::PositionChanged) {
1208 /* remove it from the list then add it back in
1209 the right place again.
1212 RegionSortByPosition cmp;
1214 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1216 if (i == regions.end()) {
1217 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1218 _name, region->name())
1219 << endmsg;
1220 return;
1223 regions.erase (i);
1224 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1227 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1229 nframes64_t delta = 0;
1231 if (what_changed & ARDOUR::PositionChanged) {
1232 delta = (nframes64_t) region->position() - (nframes64_t) region->last_position();
1235 if (what_changed & ARDOUR::LengthChanged) {
1236 delta += (nframes64_t) region->length() - (nframes64_t) region->last_length();
1239 if (delta) {
1240 possibly_splice (region->last_position() + region->last_length(), delta, region);
1243 if (holding_state ()) {
1244 pending_bounds.push_back (region);
1245 } else {
1246 if (Config->get_layer_model() == MoveAddHigher) {
1247 /* it moved or changed length, so change the timestamp */
1248 timestamp_layer_op (region);
1251 notify_length_changed ();
1252 relayer ();
1253 check_dependents (region, false);
1258 void
1259 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1261 boost::shared_ptr<Region> region (weak_region.lock());
1263 if (!region) {
1264 return;
1268 /* this makes a virtual call to the right kind of playlist ... */
1270 region_changed (what_changed, region);
1273 bool
1274 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1276 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1277 bool save = false;
1279 if (in_set_state || in_flush) {
1280 return false;
1284 if (what_changed & BoundsChanged) {
1285 region_bounds_changed (what_changed, region);
1286 save = !(_splicing || _nudging);
1289 if ((what_changed & our_interests) &&
1290 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1291 check_dependents (region, false);
1294 if (what_changed & our_interests) {
1295 save = true;
1299 return save;
1302 void
1303 Playlist::drop_regions ()
1305 RegionLock rl (this);
1306 regions.clear ();
1307 all_regions.clear ();
1310 void
1311 Playlist::clear (bool with_signals)
1314 RegionLock rl (this);
1315 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1316 pending_removes.insert (*i);
1318 regions.clear ();
1321 if (with_signals) {
1322 LengthChanged ();
1323 Modified ();
1328 /***********************************************************************
1329 FINDING THINGS
1330 **********************************************************************/
1332 Playlist::RegionList *
1333 Playlist::regions_at (nframes_t frame)
1336 RegionLock rlock (this);
1337 return find_regions_at (frame);
1340 boost::shared_ptr<Region>
1341 Playlist::top_region_at (nframes_t frame)
1344 RegionLock rlock (this);
1345 RegionList *rlist = find_regions_at (frame);
1346 boost::shared_ptr<Region> region;
1348 if (rlist->size()) {
1349 RegionSortByLayer cmp;
1350 rlist->sort (cmp);
1351 region = rlist->back();
1354 delete rlist;
1355 return region;
1358 Playlist::RegionList*
1359 Playlist::regions_to_read (nframes_t start, nframes_t end)
1361 /* Caller must hold lock */
1363 RegionList covering;
1364 set<nframes_t> to_check;
1365 set<boost::shared_ptr<Region> > unique;
1366 RegionList here;
1368 to_check.insert (start);
1369 to_check.insert (end);
1371 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1373 /* find all/any regions that span start+end */
1375 switch ((*i)->coverage (start, end)) {
1376 case OverlapNone:
1377 break;
1379 case OverlapInternal:
1380 covering.push_back (*i);
1381 break;
1383 case OverlapStart:
1384 to_check.insert ((*i)->position());
1385 covering.push_back (*i);
1386 break;
1388 case OverlapEnd:
1389 to_check.insert ((*i)->last_frame());
1390 covering.push_back (*i);
1391 break;
1393 case OverlapExternal:
1394 covering.push_back (*i);
1395 to_check.insert ((*i)->position());
1396 to_check.insert ((*i)->last_frame());
1397 break;
1400 /* don't go too far */
1402 if ((*i)->position() > end) {
1403 break;
1407 RegionList* rlist = new RegionList;
1409 /* find all the regions that cover each position .... */
1411 if (covering.size() == 1) {
1413 rlist->push_back (covering.front());
1415 } else {
1417 for (set<nframes_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1419 here.clear ();
1421 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1423 if ((*x)->covers (*t)) {
1424 here.push_back (*x);
1428 RegionSortByLayer cmp;
1429 here.sort (cmp);
1431 /* ... and get the top/transparent regions at "here" */
1433 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1435 unique.insert (*c);
1437 if ((*c)->opaque()) {
1439 /* the other regions at this position are hidden by this one */
1441 break;
1446 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1447 rlist->push_back (*s);
1450 if (rlist->size() > 1) {
1451 /* now sort by time order */
1453 RegionSortByPosition cmp;
1454 rlist->sort (cmp);
1458 return rlist;
1461 Playlist::RegionList *
1462 Playlist::find_regions_at (nframes_t frame)
1464 /* Caller must hold lock */
1466 RegionList *rlist = new RegionList;
1468 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1469 if ((*i)->covers (frame)) {
1470 rlist->push_back (*i);
1474 return rlist;
1477 Playlist::RegionList *
1478 Playlist::regions_touched (nframes_t start, nframes_t end)
1480 RegionLock rlock (this);
1481 RegionList *rlist = new RegionList;
1483 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1484 if ((*i)->coverage (start, end) != OverlapNone) {
1485 rlist->push_back (*i);
1489 return rlist;
1492 nframes64_t
1493 Playlist::find_next_transient (nframes64_t from, int dir)
1495 RegionLock rlock (this);
1496 AnalysisFeatureList points;
1497 AnalysisFeatureList these_points;
1499 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1500 if (dir > 0) {
1501 if ((*i)->last_frame() < from) {
1502 continue;
1504 } else {
1505 if ((*i)->first_frame() > from) {
1506 continue;
1510 (*i)->get_transients (these_points);
1512 /* add first frame, just, err, because */
1514 these_points.push_back ((*i)->first_frame());
1516 points.insert (points.end(), these_points.begin(), these_points.end());
1517 these_points.clear ();
1520 if (points.empty()) {
1521 return -1;
1524 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1525 bool reached = false;
1527 if (dir > 0) {
1528 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1529 if ((*x) >= from) {
1530 reached = true;
1533 if (reached && (*x) > from) {
1534 return *x;
1537 } else {
1538 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1539 if ((*x) <= from) {
1540 reached = true;
1543 if (reached && (*x) < from) {
1544 return *x;
1549 return -1;
1552 boost::shared_ptr<Region>
1553 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1555 RegionLock rlock (this);
1556 boost::shared_ptr<Region> ret;
1557 nframes_t closest = max_frames;
1560 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1562 nframes_t distance;
1563 boost::shared_ptr<Region> r = (*i);
1564 nframes_t pos = 0;
1566 switch (point) {
1567 case Start:
1568 pos = r->first_frame ();
1569 break;
1570 case End:
1571 pos = r->last_frame ();
1572 break;
1573 case SyncPoint:
1574 pos = r->sync_position ();
1575 // r->adjust_to_sync (r->first_frame());
1576 break;
1579 switch (dir) {
1580 case 1: /* forwards */
1582 if (pos >= frame) {
1583 if ((distance = pos - frame) < closest) {
1584 closest = distance;
1585 ret = r;
1589 break;
1591 default: /* backwards */
1593 if (pos <= frame) {
1594 if ((distance = frame - pos) < closest) {
1595 closest = distance;
1596 ret = r;
1599 break;
1603 return ret;
1606 nframes64_t
1607 Playlist::find_next_region_boundary (nframes64_t frame, int dir)
1609 RegionLock rlock (this);
1611 nframes64_t closest = max_frames;
1612 nframes64_t ret = -1;
1614 if (dir > 0) {
1616 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1618 boost::shared_ptr<Region> r = (*i);
1619 nframes64_t distance;
1620 nframes64_t end = r->position() + r->length();
1621 bool reset;
1623 reset = false;
1625 if (r->first_frame() > frame) {
1627 distance = r->first_frame() - frame;
1629 if (distance < closest) {
1630 ret = r->first_frame();
1631 closest = distance;
1632 reset = true;
1636 if (end > frame) {
1638 distance = end - frame;
1640 if (distance < closest) {
1641 ret = end;
1642 closest = distance;
1643 reset = true;
1647 if (reset) {
1648 break;
1652 } else {
1654 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1656 boost::shared_ptr<Region> r = (*i);
1657 nframes64_t distance;
1658 bool reset;
1660 reset = false;
1662 if (r->last_frame() < frame) {
1664 distance = frame - r->last_frame();
1666 if (distance < closest) {
1667 ret = r->last_frame();
1668 closest = distance;
1669 reset = true;
1673 if (r->first_frame() < frame) {
1674 distance = frame - r->last_frame();
1676 if (distance < closest) {
1677 ret = r->first_frame();
1678 closest = distance;
1679 reset = true;
1683 if (reset) {
1684 break;
1689 return ret;
1692 /***********************************************************************/
1696 void
1697 Playlist::mark_session_dirty ()
1699 if (!in_set_state && !holding_state ()) {
1700 _session.set_dirty();
1705 Playlist::set_state (const XMLNode& node)
1707 XMLNode *child;
1708 XMLNodeList nlist;
1709 XMLNodeConstIterator niter;
1710 XMLPropertyList plist;
1711 XMLPropertyConstIterator piter;
1712 XMLProperty *prop;
1713 boost::shared_ptr<Region> region;
1714 string region_name;
1716 in_set_state++;
1718 if (node.name() != "Playlist") {
1719 in_set_state--;
1720 return -1;
1723 freeze ();
1725 plist = node.properties();
1727 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1729 prop = *piter;
1731 if (prop->name() == X_("name")) {
1732 _name = prop->value();
1733 } else if (prop->name() == X_("orig_diskstream_id")) {
1734 _orig_diskstream_id = prop->value ();
1735 } else if (prop->name() == X_("frozen")) {
1736 _frozen = (prop->value() == X_("yes"));
1740 clear (false);
1742 nlist = node.children();
1744 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1746 child = *niter;
1748 if (child->name() == "Region") {
1750 if ((prop = child->property ("id")) == 0) {
1751 error << _("region state node has no ID, ignored") << endmsg;
1752 continue;
1755 ID id = prop->value ();
1757 if ((region = region_by_id (id))) {
1759 Change what_changed = Change (0);
1761 if (region->set_live_state (*child, what_changed, true)) {
1762 error << _("Playlist: cannot reset region state from XML") << endmsg;
1763 continue;
1766 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1767 error << _("Playlist: cannot create region from XML") << endmsg;
1768 continue;
1771 add_region (region, region->position(), 1.0);
1773 // So that layer_op ordering doesn't get screwed up
1774 region->set_last_layer_op( region->layer());
1779 notify_modified ();
1781 thaw ();
1783 /* update dependents, which was not done during add_region_internal
1784 due to in_set_state being true
1787 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1788 check_dependents (*r, false);
1791 in_set_state--;
1792 first_set_state = false;
1793 return 0;
1796 XMLNode&
1797 Playlist::get_state()
1799 return state(true);
1802 XMLNode&
1803 Playlist::get_template()
1805 return state(false);
1808 XMLNode&
1809 Playlist::state (bool full_state)
1811 XMLNode *node = new XMLNode (X_("Playlist"));
1812 char buf[64];
1814 node->add_property (X_("name"), _name);
1816 _orig_diskstream_id.print (buf, sizeof (buf));
1817 node->add_property (X_("orig_diskstream_id"), buf);
1818 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1820 if (full_state) {
1821 RegionLock rlock (this, false);
1822 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1823 node->add_child_nocopy ((*i)->get_state());
1827 if (_extra_xml) {
1828 node->add_child_copy (*_extra_xml);
1831 return *node;
1834 bool
1835 Playlist::empty() const
1837 RegionLock rlock (const_cast<Playlist *>(this), false);
1838 return regions.empty();
1841 uint32_t
1842 Playlist::n_regions() const
1844 RegionLock rlock (const_cast<Playlist *>(this), false);
1845 return regions.size();
1848 nframes_t
1849 Playlist::get_maximum_extent () const
1851 RegionLock rlock (const_cast<Playlist *>(this), false);
1852 return _get_maximum_extent ();
1855 nframes_t
1856 Playlist::_get_maximum_extent () const
1858 RegionList::const_iterator i;
1859 nframes_t max_extent = 0;
1860 nframes_t end = 0;
1862 for (i = regions.begin(); i != regions.end(); ++i) {
1863 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1864 max_extent = end;
1868 return max_extent;
1871 string
1872 Playlist::bump_name (string name, Session &session)
1874 string newname = name;
1876 do {
1877 newname = bump_name_once (newname);
1878 } while (session.playlist_by_name (newname)!=NULL);
1880 return newname;
1884 layer_t
1885 Playlist::top_layer() const
1887 RegionLock rlock (const_cast<Playlist *> (this));
1888 layer_t top = 0;
1890 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1891 top = max (top, (*i)->layer());
1893 return top;
1896 void
1897 Playlist::set_edit_mode (EditMode mode)
1899 _edit_mode = mode;
1902 /********************
1903 * Region Layering
1904 ********************/
1906 void
1907 Playlist::relayer ()
1909 RegionList::iterator i;
1910 uint32_t layer = 0;
1912 /* don't send multiple Modified notifications
1913 when multiple regions are relayered.
1916 freeze ();
1918 if (Config->get_layer_model() == MoveAddHigher ||
1919 Config->get_layer_model() == AddHigher) {
1921 RegionSortByLastLayerOp cmp;
1922 RegionList copy = regions;
1924 copy.sort (cmp);
1926 for (i = copy.begin(); i != copy.end(); ++i) {
1927 (*i)->set_layer (layer++);
1930 } else {
1932 /* Session::LaterHigher model */
1934 for (i = regions.begin(); i != regions.end(); ++i) {
1935 (*i)->set_layer (layer++);
1939 /* sending Modified means that various kinds of layering
1940 models operate correctly at the GUI
1941 level. slightly inefficient, but only slightly.
1943 We force a Modified signal here in case no layers actually
1944 changed.
1947 notify_modified ();
1949 thaw ();
1952 /* XXX these layer functions are all deprecated */
1954 void
1955 Playlist::raise_region (boost::shared_ptr<Region> region)
1957 uint32_t rsz = regions.size();
1958 layer_t target = region->layer() + 1U;
1960 if (target >= rsz) {
1961 /* its already at the effective top */
1962 return;
1965 move_region_to_layer (target, region, 1);
1968 void
1969 Playlist::lower_region (boost::shared_ptr<Region> region)
1971 if (region->layer() == 0) {
1972 /* its already at the bottom */
1973 return;
1976 layer_t target = region->layer() - 1U;
1978 move_region_to_layer (target, region, -1);
1981 void
1982 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1984 /* does nothing useful if layering mode is later=higher */
1985 if ((Config->get_layer_model() == MoveAddHigher) ||
1986 (Config->get_layer_model() == AddHigher)) {
1987 timestamp_layer_op (region);
1988 relayer ();
1992 void
1993 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1995 /* does nothing useful if layering mode is later=higher */
1996 if ((Config->get_layer_model() == MoveAddHigher) ||
1997 (Config->get_layer_model() == AddHigher)) {
1998 region->set_last_layer_op (0);
1999 relayer ();
2004 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2006 RegionList::iterator i;
2007 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2008 list<LayerInfo> layerinfo;
2009 layer_t dest;
2012 RegionLock rlock (const_cast<Playlist *> (this));
2014 for (i = regions.begin(); i != regions.end(); ++i) {
2016 if (region == *i) {
2017 continue;
2020 if (dir > 0) {
2022 /* region is moving up, move all regions on intermediate layers
2023 down 1
2026 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2027 dest = (*i)->layer() - 1;
2028 } else {
2029 /* not affected */
2030 continue;
2032 } else {
2034 /* region is moving down, move all regions on intermediate layers
2035 up 1
2038 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2039 dest = (*i)->layer() + 1;
2040 } else {
2041 /* not affected */
2042 continue;
2046 LayerInfo newpair;
2048 newpair.first = *i;
2049 newpair.second = dest;
2051 layerinfo.push_back (newpair);
2055 /* now reset the layers without holding the region lock */
2057 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2058 x->first->set_layer (x->second);
2061 region->set_layer (target_layer);
2063 #if 0
2064 /* now check all dependents */
2066 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2067 check_dependents (x->first, false);
2070 check_dependents (region, false);
2071 #endif
2073 return 0;
2076 void
2077 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
2079 RegionList::iterator i;
2080 nframes_t new_pos;
2081 bool moved = false;
2083 _nudging = true;
2086 RegionLock rlock (const_cast<Playlist *> (this));
2088 for (i = regions.begin(); i != regions.end(); ++i) {
2090 if ((*i)->position() >= start) {
2092 if (forwards) {
2094 if ((*i)->last_frame() > max_frames - distance) {
2095 new_pos = max_frames - (*i)->length();
2096 } else {
2097 new_pos = (*i)->position() + distance;
2100 } else {
2102 if ((*i)->position() > distance) {
2103 new_pos = (*i)->position() - distance;
2104 } else {
2105 new_pos = 0;
2109 (*i)->set_position (new_pos, this);
2110 moved = true;
2115 if (moved) {
2116 _nudging = false;
2117 notify_length_changed ();
2122 boost::shared_ptr<Region>
2123 Playlist::find_region (const ID& id) const
2125 RegionLock rlock (const_cast<Playlist*> (this));
2127 /* searches all regions currently in use by the playlist */
2129 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2130 if ((*i)->id() == id) {
2131 return *i;
2135 return boost::shared_ptr<Region> ();
2138 boost::shared_ptr<Region>
2139 Playlist::region_by_id (ID id)
2141 /* searches all regions ever added to this playlist */
2143 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2144 if ((*i)->id() == id) {
2145 return *i;
2148 return boost::shared_ptr<Region> ();
2151 void
2152 Playlist::dump () const
2154 boost::shared_ptr<Region> r;
2156 cerr << "Playlist \"" << _name << "\" " << endl
2157 << regions.size() << " regions "
2158 << endl;
2160 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2161 r = *i;
2162 cerr << " " << r->name() << " ["
2163 << r->start() << "+" << r->length()
2164 << "] at "
2165 << r->position()
2166 << " on layer "
2167 << r->layer ()
2168 << endl;
2172 void
2173 Playlist::set_frozen (bool yn)
2175 _frozen = yn;
2178 void
2179 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2181 // struct timeval tv;
2182 // gettimeofday (&tv, 0);
2183 region->set_last_layer_op (++layer_op_counter);
2187 void
2188 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2190 bool moved = false;
2191 nframes_t new_pos;
2193 if (region->locked()) {
2194 return;
2197 _shuffling = true;
2200 RegionLock rlock (const_cast<Playlist*> (this));
2203 if (dir > 0) {
2205 RegionList::iterator next;
2207 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2208 if ((*i) == region) {
2209 next = i;
2210 ++next;
2212 if (next != regions.end()) {
2214 if ((*next)->locked()) {
2215 break;
2218 if ((*next)->position() != region->last_frame() + 1) {
2219 /* they didn't used to touch, so after shuffle,
2220 just have them swap positions.
2222 new_pos = (*next)->position();
2223 } else {
2224 /* they used to touch, so after shuffle,
2225 make sure they still do. put the earlier
2226 region where the later one will end after
2227 it is moved.
2229 new_pos = region->position() + (*next)->length();
2232 (*next)->set_position (region->position(), this);
2233 region->set_position (new_pos, this);
2235 /* avoid a full sort */
2237 regions.erase (i); // removes the region from the list */
2238 next++;
2239 regions.insert (next, region); // adds it back after next
2241 moved = true;
2243 break;
2246 } else {
2248 RegionList::iterator prev = regions.end();
2250 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2251 if ((*i) == region) {
2253 if (prev != regions.end()) {
2255 if ((*prev)->locked()) {
2256 break;
2259 if (region->position() != (*prev)->last_frame() + 1) {
2260 /* they didn't used to touch, so after shuffle,
2261 just have them swap positions.
2263 new_pos = region->position();
2264 } else {
2265 /* they used to touch, so after shuffle,
2266 make sure they still do. put the earlier
2267 one where the later one will end after
2269 new_pos = (*prev)->position() + region->length();
2272 region->set_position ((*prev)->position(), this);
2273 (*prev)->set_position (new_pos, this);
2275 /* avoid a full sort */
2277 regions.erase (i); // remove region
2278 regions.insert (prev, region); // insert region before prev
2280 moved = true;
2283 break;
2289 _shuffling = false;
2291 if (moved) {
2293 relayer ();
2294 check_dependents (region, false);
2296 notify_modified();
2301 bool
2302 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2304 RegionLock rlock (const_cast<Playlist*> (this));
2306 if (regions.size() > 1) {
2307 return true;
2310 return false;
2313 void
2314 Playlist::update_after_tempo_map_change ()
2316 RegionLock rlock (const_cast<Playlist*> (this));
2317 RegionList copy (regions);
2319 freeze ();
2321 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2322 (*i)->update_position_after_tempo_map_change ();
2325 thaw ();