possible fix for race between diskstream buffer overwrite and channel setup
[ardour2.git] / libs / ardour / playlist.cc
blobc71d53deca6dc009d6ac769b66be5ed695847b64
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 <boost/lexical_cast.hpp>
30 #include "pbd/failed_constructor.h"
31 #include "pbd/stateful_diff_command.h"
32 #include "pbd/xml++.h"
34 #include "ardour/debug.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"
41 #include "ardour/session_playlists.h"
43 #include "i18n.h"
45 using namespace std;
46 using namespace ARDOUR;
47 using namespace PBD;
49 namespace ARDOUR {
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> regions;
55 struct ShowMeTheList {
56 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
57 ~ShowMeTheList () {
58 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
60 boost::shared_ptr<Playlist> playlist;
61 string name;
64 struct RegionSortByLayer {
65 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
66 return a->layer() < b->layer();
70 struct RegionSortByLayerWithPending {
71 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
73 double p = a->layer ();
74 if (a->pending_explicit_relayer()) {
75 p += 0.5;
78 double q = b->layer ();
79 if (b->pending_explicit_relayer()) {
80 q += 0.5;
83 return p < q;
87 struct RegionSortByPosition {
88 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
89 return a->position() < b->position();
93 struct RegionSortByLastLayerOp {
94 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
95 return a->last_layer_op() < b->last_layer_op();
99 void
100 Playlist::make_property_quarks ()
102 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n", Properties::regions.property_id));
106 RegionListProperty::RegionListProperty (Playlist& pl)
107 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
108 , _playlist (pl)
112 boost::shared_ptr<Region>
113 RegionListProperty::lookup_id (const ID& id)
115 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
117 if (!ret) {
118 ret = RegionFactory::region_by_id (id);
121 return ret;
124 RegionListProperty*
125 RegionListProperty::copy_for_history () const
127 RegionListProperty* copy = new RegionListProperty (_playlist);
128 /* this is all we need */
129 copy->_change = _change;
130 return copy;
133 void
134 RegionListProperty::diff (PropertyList& undo, PropertyList& redo) const
136 if (changed()) {
137 /* list of the removed/added regions since clear_history() was last called */
138 RegionListProperty* a = copy_for_history ();
140 /* the same list, but with removed/added lists swapped (for undo purposes) */
141 RegionListProperty* b = copy_for_history ();
142 b->invert_changes ();
144 undo.add (b);
145 redo.add (a);
149 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
150 : SessionObject(sess, nom)
151 , regions (*this)
152 , _type(type)
154 init (hide);
155 first_set_state = false;
156 _name = nom;
157 _set_sort_id ();
161 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
162 : SessionObject(sess, "unnamed playlist")
163 , regions (*this)
164 , _type(type)
167 const XMLProperty* prop = node.property("type");
168 assert(!prop || DataType(prop->value()) == _type);
170 init (hide);
171 _name = "unnamed"; /* reset by set_state */
172 _set_sort_id ();
174 /* set state called by derived class */
177 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
178 : SessionObject(other->_session, namestr)
179 , regions (*this)
180 , _type(other->_type)
181 , _orig_diskstream_id(other->_orig_diskstream_id)
183 init (hide);
185 RegionList tmp;
186 other->copy_regions (tmp);
188 in_set_state++;
190 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
191 add_region_internal( (*x), (*x)->position());
194 in_set_state--;
196 _splicing = other->_splicing;
197 _nudging = other->_nudging;
198 _edit_mode = other->_edit_mode;
200 in_set_state = 0;
201 first_set_state = false;
202 in_flush = false;
203 in_partition = false;
204 subcnt = 0;
205 _read_data_count = 0;
206 _frozen = other->_frozen;
208 layer_op_counter = other->layer_op_counter;
209 freeze_length = other->freeze_length;
212 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
213 : SessionObject(other->_session, str)
214 , regions (*this)
215 , _type(other->_type)
216 , _orig_diskstream_id(other->_orig_diskstream_id)
218 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
220 framepos_t end = start + cnt - 1;
222 init (hide);
224 in_set_state++;
226 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
228 boost::shared_ptr<Region> region;
229 boost::shared_ptr<Region> new_region;
230 frameoffset_t offset = 0;
231 framepos_t position = 0;
232 framecnt_t len = 0;
233 string new_name;
234 OverlapType overlap;
236 region = *i;
238 overlap = region->coverage (start, end);
240 switch (overlap) {
241 case OverlapNone:
242 continue;
244 case OverlapInternal:
245 offset = start - region->position();
246 position = 0;
247 len = cnt;
248 break;
250 case OverlapStart:
251 offset = 0;
252 position = region->position() - start;
253 len = end - region->position();
254 break;
256 case OverlapEnd:
257 offset = start - region->position();
258 position = 0;
259 len = region->length() - offset;
260 break;
262 case OverlapExternal:
263 offset = 0;
264 position = region->position() - start;
265 len = region->length();
266 break;
269 RegionFactory::region_name (new_name, region->name(), false);
271 PropertyList plist;
273 plist.add (Properties::start, region->start() + offset);
274 plist.add (Properties::length, len);
275 plist.add (Properties::name, new_name);
276 plist.add (Properties::layer, region->layer());
278 new_region = RegionFactory::RegionFactory::create (region, plist);
280 add_region_internal (new_region, position);
283 in_set_state--;
284 first_set_state = false;
286 /* this constructor does NOT notify others (session) */
289 void
290 Playlist::use ()
292 ++_refcnt;
293 InUse (true); /* EMIT SIGNAL */
296 void
297 Playlist::release ()
299 if (_refcnt > 0) {
300 _refcnt--;
303 if (_refcnt == 0) {
304 InUse (false); /* EMIT SIGNAL */
308 void
309 Playlist::copy_regions (RegionList& newlist) const
311 RegionLock rlock (const_cast<Playlist *> (this));
313 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
314 newlist.push_back (RegionFactory::RegionFactory::create (*i));
318 void
319 Playlist::init (bool hide)
321 add_property (regions);
322 _xml_node_name = X_("Playlist");
324 g_atomic_int_set (&block_notifications, 0);
325 g_atomic_int_set (&ignore_state_changes, 0);
326 pending_contents_change = false;
327 pending_length = false;
328 pending_layering = false;
329 first_set_state = true;
330 _refcnt = 0;
331 _hidden = hide;
332 _splicing = false;
333 _shuffling = false;
334 _nudging = false;
335 in_set_state = 0;
336 in_update = false;
337 _edit_mode = Config->get_edit_mode();
338 in_flush = false;
339 in_partition = false;
340 subcnt = 0;
341 _read_data_count = 0;
342 _frozen = false;
343 layer_op_counter = 0;
344 freeze_length = 0;
345 _explicit_relayering = false;
347 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
348 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
350 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
353 Playlist::~Playlist ()
355 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
358 RegionLock rl (this);
360 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
361 (*i)->set_playlist (boost::shared_ptr<Playlist>());
365 /* GoingAway must be emitted by derived classes */
368 void
369 Playlist::_set_sort_id ()
372 Playlists are given names like <track name>.<id>
373 or <track name>.<edit group name>.<id> where id
374 is an integer. We extract the id and sort by that.
377 size_t dot_position = _name.val().find_last_of(".");
379 if (dot_position == string::npos) {
380 _sort_id = 0;
381 } else {
382 string t = _name.val().substr(dot_position + 1);
384 try {
385 _sort_id = boost::lexical_cast<int>(t);
388 catch (boost::bad_lexical_cast e) {
389 _sort_id = 0;
394 bool
395 Playlist::set_name (const string& str)
397 /* in a typical situation, a playlist is being used
398 by one diskstream and also is referenced by the
399 Session. if there are more references than that,
400 then don't change the name.
403 if (_refcnt > 2) {
404 return false;
407 bool ret = SessionObject::set_name(str);
408 if (ret) {
409 _set_sort_id ();
411 return ret;
414 /***********************************************************************
415 CHANGE NOTIFICATION HANDLING
417 Notifications must be delayed till the region_lock is released. This
418 is necessary because handlers for the signals may need to acquire
419 the lock (e.g. to read from the playlist).
420 ***********************************************************************/
422 void
423 Playlist::begin_undo ()
425 in_update = true;
426 freeze ();
429 void
430 Playlist::end_undo ()
432 thaw ();
433 in_update = false;
436 void
437 Playlist::freeze ()
439 delay_notifications ();
440 g_atomic_int_inc (&ignore_state_changes);
443 void
444 Playlist::thaw ()
446 g_atomic_int_dec_and_test (&ignore_state_changes);
447 release_notifications ();
451 void
452 Playlist::delay_notifications ()
454 g_atomic_int_inc (&block_notifications);
455 freeze_length = _get_maximum_extent();
458 void
459 Playlist::release_notifications ()
461 if (g_atomic_int_dec_and_test (&block_notifications)) {
462 flush_notifications ();
467 void
468 Playlist::notify_contents_changed ()
470 if (holding_state ()) {
471 pending_contents_change = true;
472 } else {
473 pending_contents_change = false;
474 ContentsChanged(); /* EMIT SIGNAL */
478 void
479 Playlist::notify_layering_changed ()
481 if (holding_state ()) {
482 pending_layering = true;
483 } else {
484 pending_layering = false;
485 LayeringChanged(); /* EMIT SIGNAL */
489 void
490 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
492 if (holding_state ()) {
493 pending_removes.insert (r);
494 pending_contents_change = true;
495 pending_length = true;
496 } else {
497 /* this might not be true, but we have to act
498 as though it could be.
500 pending_length = false;
501 LengthChanged (); /* EMIT SIGNAL */
502 pending_contents_change = false;
503 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
504 ContentsChanged (); /* EMIT SIGNAL */
508 void
509 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
511 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
513 if (holding_state ()) {
515 pending_range_moves.push_back (move);
517 } else {
519 list< Evoral::RangeMove<framepos_t> > m;
520 m.push_back (move);
521 RangesMoved (m);
526 void
527 Playlist::notify_region_added (boost::shared_ptr<Region> r)
529 /* the length change might not be true, but we have to act
530 as though it could be.
533 if (holding_state()) {
534 pending_adds.insert (r);
535 pending_contents_change = true;
536 pending_length = true;
537 } else {
538 pending_length = false;
539 LengthChanged (); /* EMIT SIGNAL */
540 pending_contents_change = false;
541 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
542 ContentsChanged (); /* EMIT SIGNAL */
546 void
547 Playlist::notify_length_changed ()
549 if (holding_state ()) {
550 pending_length = true;
551 } else {
552 pending_length = false;
553 LengthChanged(); /* EMIT SIGNAL */
554 pending_contents_change = false;
555 ContentsChanged (); /* EMIT SIGNAL */
559 void
560 Playlist::flush_notifications ()
562 set<boost::shared_ptr<Region> > dependent_checks_needed;
563 set<boost::shared_ptr<Region> >::iterator s;
564 uint32_t regions_changed = false;
565 bool check_length = false;
566 framecnt_t old_length = 0;
568 if (in_flush) {
569 return;
572 in_flush = true;
574 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
575 regions_changed = true;
576 if (!pending_length) {
577 old_length = _get_maximum_extent ();
578 check_length = true;
582 /* we have no idea what order the regions ended up in pending
583 bounds (it could be based on selection order, for example).
584 so, to preserve layering in the "most recently moved is higher"
585 model, sort them by existing layer, then timestamp them.
588 // RegionSortByLayer cmp;
589 // pending_bounds.sort (cmp);
591 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
592 if (_session.config.get_layer_model() == MoveAddHigher) {
593 timestamp_layer_op (*r);
595 dependent_checks_needed.insert (*r);
598 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
599 remove_dependents (*s);
600 // cerr << _name << " sends RegionRemoved\n";
601 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
604 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
605 // cerr << _name << " sends RegionAdded\n";
606 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
607 dependent_checks_needed.insert (*s);
610 if (check_length) {
611 if (old_length != _get_maximum_extent()) {
612 pending_length = true;
613 // cerr << _name << " length has changed\n";
617 if (pending_length || (freeze_length != _get_maximum_extent())) {
618 pending_length = false;
619 // cerr << _name << " sends LengthChanged\n";
620 LengthChanged(); /* EMIT SIGNAL */
623 if (regions_changed || pending_contents_change) {
624 if (!in_set_state) {
625 relayer ();
627 pending_contents_change = false;
628 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
629 ContentsChanged (); /* EMIT SIGNAL */
630 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
633 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
634 check_dependents (*s, false);
637 if (!pending_range_moves.empty ()) {
638 // cerr << _name << " sends RangesMoved\n";
639 RangesMoved (pending_range_moves);
642 clear_pending ();
644 in_flush = false;
647 void
648 Playlist::clear_pending ()
650 pending_adds.clear ();
651 pending_removes.clear ();
652 pending_bounds.clear ();
653 pending_range_moves.clear ();
654 pending_contents_change = false;
655 pending_length = false;
658 /*************************************************************
659 PLAYLIST OPERATIONS
660 *************************************************************/
662 void
663 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
665 RegionLock rlock (this);
666 times = fabs (times);
668 int itimes = (int) floor (times);
670 framepos_t pos = position;
672 if (times == 1 && auto_partition){
673 partition(pos, (pos + region->length()), true);
676 if (itimes >= 1) {
677 add_region_internal (region, pos);
678 pos += region->length();
679 --itimes;
683 /* note that itimes can be zero if we being asked to just
684 insert a single fraction of the region.
687 for (int i = 0; i < itimes; ++i) {
688 boost::shared_ptr<Region> copy = RegionFactory::create (region);
689 add_region_internal (copy, pos);
690 pos += region->length();
693 framecnt_t length = 0;
695 if (floor (times) != times) {
696 length = (framecnt_t) floor (region->length() * (times - floor (times)));
697 string name;
698 RegionFactory::region_name (name, region->name(), false);
701 PropertyList plist;
703 plist.add (Properties::start, region->start());
704 plist.add (Properties::length, length);
705 plist.add (Properties::name, name);
706 plist.add (Properties::layer, region->layer());
708 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
709 add_region_internal (sub, pos);
713 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
716 void
717 Playlist::set_region_ownership ()
719 RegionLock rl (this);
720 RegionList::iterator i;
721 boost::weak_ptr<Playlist> pl (shared_from_this());
723 for (i = regions.begin(); i != regions.end(); ++i) {
724 (*i)->set_playlist (pl);
728 bool
729 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
731 if (region->data_type() != _type){
732 return false;
735 RegionSortByPosition cmp;
737 framecnt_t old_length = 0;
739 if (!holding_state()) {
740 old_length = _get_maximum_extent();
743 if (!first_set_state) {
744 boost::shared_ptr<Playlist> foo (shared_from_this());
745 region->set_playlist (boost::weak_ptr<Playlist>(foo));
748 region->set_position (position, this);
750 timestamp_layer_op (region);
752 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
753 all_regions.insert (region);
755 possibly_splice_unlocked (position, region->length(), region);
757 if (!holding_state ()) {
758 /* layers get assigned from XML state, and are not reset during undo/redo */
759 relayer ();
762 /* we need to notify the existence of new region before checking dependents. Ick. */
764 notify_region_added (region);
766 if (!holding_state ()) {
768 check_dependents (region, false);
770 if (old_length != _get_maximum_extent()) {
771 notify_length_changed ();
775 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
777 return true;
780 void
781 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
783 RegionLock rlock (this);
785 bool old_sp = _splicing;
786 _splicing = true;
788 remove_region_internal (old);
789 add_region_internal (newr, pos);
791 _splicing = old_sp;
793 possibly_splice_unlocked (pos, old->length() - newr->length());
796 void
797 Playlist::remove_region (boost::shared_ptr<Region> region)
799 RegionLock rlock (this);
800 remove_region_internal (region);
804 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
806 RegionList::iterator i;
807 framecnt_t old_length = 0;
808 int ret = -1;
810 if (!holding_state()) {
811 old_length = _get_maximum_extent();
814 if (!in_set_state) {
815 /* unset playlist */
816 region->set_playlist (boost::weak_ptr<Playlist>());
819 /* XXX should probably freeze here .... */
821 for (i = regions.begin(); i != regions.end(); ++i) {
822 if (*i == region) {
824 framepos_t pos = (*i)->position();
825 framecnt_t distance = (*i)->length();
827 regions.erase (i);
829 possibly_splice_unlocked (pos, -distance);
831 if (!holding_state ()) {
832 relayer ();
833 remove_dependents (region);
835 if (old_length != _get_maximum_extent()) {
836 notify_length_changed ();
840 notify_region_removed (region);
841 ret = 0;
842 break;
846 /* XXX and thaw ... */
848 return ret;
851 void
852 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
854 if (Config->get_use_overlap_equivalency()) {
855 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
856 if ((*i)->overlap_equivalent (other)) {
857 results.push_back ((*i));
860 } else {
861 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
862 if ((*i)->equivalent (other)) {
863 results.push_back ((*i));
869 void
870 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
872 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
874 if ((*i) && (*i)->region_list_equivalent (other)) {
875 results.push_back (*i);
880 void
881 Playlist::partition (framepos_t start, framepos_t end, bool cut)
883 RegionList thawlist;
885 partition_internal (start, end, cut, thawlist);
887 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
888 (*i)->resume_property_changes ();
892 void
893 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
895 RegionList new_regions;
898 RegionLock rlock (this);
900 boost::shared_ptr<Region> region;
901 boost::shared_ptr<Region> current;
902 string new_name;
903 RegionList::iterator tmp;
904 OverlapType overlap;
905 framepos_t pos1, pos2, pos3, pos4;
907 in_partition = true;
909 /* need to work from a copy, because otherwise the regions we add during the process
910 get operated on as well.
913 RegionList copy = regions.rlist();
915 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
917 tmp = i;
918 ++tmp;
920 current = *i;
922 if (current->first_frame() >= start && current->last_frame() < end) {
924 if (cutting) {
925 remove_region_internal (current);
928 continue;
931 /* coverage will return OverlapStart if the start coincides
932 with the end point. we do not partition such a region,
933 so catch this special case.
936 if (current->first_frame() >= end) {
937 continue;
940 if ((overlap = current->coverage (start, end)) == OverlapNone) {
941 continue;
944 pos1 = current->position();
945 pos2 = start;
946 pos3 = end;
947 pos4 = current->last_frame();
949 if (overlap == OverlapInternal) {
950 /* split: we need 3 new regions, the front, middle and end.
951 cut: we need 2 regions, the front and end.
955 start end
956 ---------------*************************------------
957 P1 P2 P3 P4
958 SPLIT:
959 ---------------*****++++++++++++++++====------------
961 ---------------*****----------------====------------
965 if (!cutting) {
966 /* "middle" ++++++ */
968 RegionFactory::region_name (new_name, current->name(), false);
970 PropertyList plist;
972 plist.add (Properties::start, current->start() + (pos2 - pos1));
973 plist.add (Properties::length, pos3 - pos2);
974 plist.add (Properties::name, new_name);
975 plist.add (Properties::layer, regions.size());
976 plist.add (Properties::automatic, true);
977 plist.add (Properties::left_of_split, true);
978 plist.add (Properties::right_of_split, true);
980 region = RegionFactory::create (current, plist);
981 add_region_internal (region, start);
982 new_regions.push_back (region);
985 /* "end" ====== */
987 RegionFactory::region_name (new_name, current->name(), false);
989 PropertyList plist;
991 plist.add (Properties::start, current->start() + (pos3 - pos1));
992 plist.add (Properties::length, pos4 - pos3);
993 plist.add (Properties::name, new_name);
994 plist.add (Properties::layer, regions.size());
995 plist.add (Properties::automatic, true);
996 plist.add (Properties::right_of_split, true);
998 region = RegionFactory::create (current, plist);
1000 add_region_internal (region, end);
1001 new_regions.push_back (region);
1003 /* "front" ***** */
1005 current->suspend_property_changes ();
1006 thawlist.push_back (current);
1007 current->trim_end (pos2, this);
1009 } else if (overlap == OverlapEnd) {
1012 start end
1013 ---------------*************************------------
1014 P1 P2 P4 P3
1015 SPLIT:
1016 ---------------**************+++++++++++------------
1017 CUT:
1018 ---------------**************-----------------------
1021 if (!cutting) {
1023 /* end +++++ */
1025 RegionFactory::region_name (new_name, current->name(), false);
1027 PropertyList plist;
1029 plist.add (Properties::start, current->start() + (pos2 - pos1));
1030 plist.add (Properties::length, pos4 - pos2);
1031 plist.add (Properties::name, new_name);
1032 plist.add (Properties::layer, regions.size());
1033 plist.add (Properties::automatic, true);
1034 plist.add (Properties::left_of_split, true);
1036 region = RegionFactory::create (current, plist);
1038 add_region_internal (region, start);
1039 new_regions.push_back (region);
1042 /* front ****** */
1044 current->suspend_property_changes ();
1045 thawlist.push_back (current);
1046 current->trim_end (pos2, this);
1048 } else if (overlap == OverlapStart) {
1050 /* split: we need 2 regions: the front and the end.
1051 cut: just trim current to skip the cut area
1055 start end
1056 ---------------*************************------------
1057 P2 P1 P3 P4
1059 SPLIT:
1060 ---------------****+++++++++++++++++++++------------
1061 CUT:
1062 -------------------*********************------------
1066 if (!cutting) {
1067 /* front **** */
1068 RegionFactory::region_name (new_name, current->name(), false);
1070 PropertyList plist;
1072 plist.add (Properties::start, current->start());
1073 plist.add (Properties::length, pos3 - pos1);
1074 plist.add (Properties::name, new_name);
1075 plist.add (Properties::layer, regions.size());
1076 plist.add (Properties::automatic, true);
1077 plist.add (Properties::right_of_split, true);
1079 region = RegionFactory::create (current, plist);
1081 add_region_internal (region, pos1);
1082 new_regions.push_back (region);
1085 /* end */
1087 current->suspend_property_changes ();
1088 thawlist.push_back (current);
1089 current->trim_front (pos3, this);
1090 } else if (overlap == OverlapExternal) {
1092 /* split: no split required.
1093 cut: remove the region.
1097 start end
1098 ---------------*************************------------
1099 P2 P1 P3 P4
1101 SPLIT:
1102 ---------------*************************------------
1103 CUT:
1104 ----------------------------------------------------
1108 if (cutting) {
1109 remove_region_internal (current);
1112 new_regions.push_back (current);
1116 in_partition = false;
1119 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1120 check_dependents (*i, false);
1124 boost::shared_ptr<Playlist>
1125 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1127 boost::shared_ptr<Playlist> ret;
1128 boost::shared_ptr<Playlist> pl;
1129 framepos_t start;
1131 if (ranges.empty()) {
1132 return boost::shared_ptr<Playlist>();
1135 start = ranges.front().start;
1137 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1139 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1141 if (i == ranges.begin()) {
1142 ret = pl;
1143 } else {
1145 /* paste the next section into the nascent playlist,
1146 offset to reflect the start of the first range we
1147 chopped.
1150 ret->paste (pl, (*i).start - start, 1.0f);
1154 return ret;
1157 boost::shared_ptr<Playlist>
1158 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1160 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1161 return cut_copy (pmf, ranges, result_is_hidden);
1164 boost::shared_ptr<Playlist>
1165 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1167 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1168 return cut_copy (pmf, ranges, result_is_hidden);
1171 boost::shared_ptr<Playlist>
1172 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1174 boost::shared_ptr<Playlist> the_copy;
1175 RegionList thawlist;
1176 char buf[32];
1178 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1179 string new_name = _name;
1180 new_name += '.';
1181 new_name += buf;
1183 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1184 return boost::shared_ptr<Playlist>();
1187 partition_internal (start, start+cnt-1, true, thawlist);
1189 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1190 (*i)->resume_property_changes();
1193 return the_copy;
1196 boost::shared_ptr<Playlist>
1197 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1199 char buf[32];
1201 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1202 string new_name = _name;
1203 new_name += '.';
1204 new_name += buf;
1206 cnt = min (_get_maximum_extent() - start, cnt);
1207 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1211 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1213 times = fabs (times);
1216 RegionLock rl1 (this);
1217 RegionLock rl2 (other.get());
1219 framecnt_t old_length = _get_maximum_extent();
1221 int itimes = (int) floor (times);
1222 framepos_t pos = position;
1223 framecnt_t shift = other->_get_maximum_extent();
1224 layer_t top_layer = regions.size();
1226 while (itimes--) {
1227 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1228 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
1230 /* put these new regions on top of all existing ones, but preserve
1231 the ordering they had in the original playlist.
1234 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1235 add_region_internal (copy_of_region, copy_of_region->position() + pos);
1237 pos += shift;
1241 /* XXX shall we handle fractional cases at some point? */
1243 if (old_length != _get_maximum_extent()) {
1244 notify_length_changed ();
1250 return 0;
1254 void
1255 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1257 times = fabs (times);
1259 RegionLock rl (this);
1260 int itimes = (int) floor (times);
1261 framepos_t pos = position;
1263 while (itimes--) {
1264 boost::shared_ptr<Region> copy = RegionFactory::create (region);
1265 add_region_internal (copy, pos);
1266 pos += region->length();
1269 if (floor (times) != times) {
1270 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1271 string name;
1272 RegionFactory::region_name (name, region->name(), false);
1275 PropertyList plist;
1277 plist.add (Properties::start, region->start());
1278 plist.add (Properties::length, length);
1279 plist.add (Properties::name, name);
1281 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1282 add_region_internal (sub, pos);
1287 void
1288 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1290 RegionLock rlock (this);
1291 RegionList copy (regions.rlist());
1292 RegionList fixup;
1294 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1296 if ((*r)->last_frame() < at) {
1297 /* too early */
1298 continue;
1301 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1302 /* intersected region */
1303 if (!move_intersected) {
1304 continue;
1308 /* do not move regions glued to music time - that
1309 has to be done separately.
1312 if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) {
1313 fixup.push_back (*r);
1314 continue;
1317 (*r)->set_position ((*r)->position() + distance, this);
1320 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1321 (*r)->recompute_position_from_lock_style ();
1325 void
1326 Playlist::split (framepos_t at)
1328 RegionLock rlock (this);
1329 RegionList copy (regions.rlist());
1331 /* use a copy since this operation can modify the region list
1334 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1335 _split_region (*r, at);
1339 void
1340 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1342 RegionLock rl (this);
1343 _split_region (region, playlist_position);
1346 void
1347 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1349 if (!region->covers (playlist_position)) {
1350 return;
1353 if (region->position() == playlist_position ||
1354 region->last_frame() == playlist_position) {
1355 return;
1358 boost::shared_ptr<Region> left;
1359 boost::shared_ptr<Region> right;
1360 frameoffset_t before;
1361 frameoffset_t after;
1362 string before_name;
1363 string after_name;
1365 /* split doesn't change anything about length, so don't try to splice */
1367 bool old_sp = _splicing;
1368 _splicing = true;
1370 before = playlist_position - region->position();
1371 after = region->length() - before;
1373 RegionFactory::region_name (before_name, region->name(), false);
1376 PropertyList plist;
1378 plist.add (Properties::start, region->start());
1379 plist.add (Properties::length, before);
1380 plist.add (Properties::name, before_name);
1381 plist.add (Properties::left_of_split, true);
1383 left = RegionFactory::create (region, plist);
1386 RegionFactory::region_name (after_name, region->name(), false);
1389 PropertyList plist;
1391 plist.add (Properties::start, region->start() + before);
1392 plist.add (Properties::length, after);
1393 plist.add (Properties::name, after_name);
1394 plist.add (Properties::right_of_split, true);
1396 right = RegionFactory::create (region, plist);
1399 add_region_internal (left, region->position());
1400 add_region_internal (right, region->position() + before);
1402 uint64_t orig_layer_op = region->last_layer_op();
1403 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1404 if ((*i)->last_layer_op() > orig_layer_op) {
1405 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1409 left->set_last_layer_op ( orig_layer_op );
1410 right->set_last_layer_op ( orig_layer_op + 1);
1412 layer_op_counter++;
1414 finalize_split_region (region, left, right);
1416 remove_region_internal (region);
1418 _splicing = old_sp;
1421 void
1422 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1424 if (_splicing || in_set_state) {
1425 /* don't respond to splicing moves or state setting */
1426 return;
1429 if (_edit_mode == Splice) {
1430 splice_locked (at, distance, exclude);
1434 void
1435 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1437 if (_splicing || in_set_state) {
1438 /* don't respond to splicing moves or state setting */
1439 return;
1442 if (_edit_mode == Splice) {
1443 splice_unlocked (at, distance, exclude);
1447 void
1448 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1451 RegionLock rl (this);
1452 core_splice (at, distance, exclude);
1456 void
1457 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1459 core_splice (at, distance, exclude);
1462 void
1463 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1465 _splicing = true;
1467 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1469 if (exclude && (*i) == exclude) {
1470 continue;
1473 if ((*i)->position() >= at) {
1474 framepos_t new_pos = (*i)->position() + distance;
1475 if (new_pos < 0) {
1476 new_pos = 0;
1477 } else if (new_pos >= max_frames - (*i)->length()) {
1478 new_pos = max_frames - (*i)->length();
1481 (*i)->set_position (new_pos, this);
1485 _splicing = false;
1487 notify_length_changed ();
1490 void
1491 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1493 if (in_set_state || _splicing || _nudging || _shuffling) {
1494 return;
1497 if (what_changed.contains (Properties::position)) {
1499 /* remove it from the list then add it back in
1500 the right place again.
1503 RegionSortByPosition cmp;
1505 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1507 if (i == regions.end()) {
1508 /* the region bounds are being modified but its not currently
1509 in the region list. we will use its bounds correctly when/if
1510 it is added
1512 return;
1515 regions.erase (i);
1516 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1519 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1521 frameoffset_t delta = 0;
1523 if (what_changed.contains (Properties::position)) {
1524 delta = region->position() - region->last_position();
1527 if (what_changed.contains (Properties::length)) {
1528 delta += region->length() - region->last_length();
1531 if (delta) {
1532 possibly_splice (region->last_position() + region->last_length(), delta, region);
1535 if (holding_state ()) {
1536 pending_bounds.push_back (region);
1537 } else {
1538 if (_session.config.get_layer_model() == MoveAddHigher) {
1539 /* it moved or changed length, so change the timestamp */
1540 timestamp_layer_op (region);
1543 notify_length_changed ();
1544 relayer ();
1545 check_dependents (region, false);
1550 void
1551 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1553 boost::shared_ptr<Region> region (weak_region.lock());
1555 if (!region) {
1556 return;
1559 /* this makes a virtual call to the right kind of playlist ... */
1561 region_changed (what_changed, region);
1564 bool
1565 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1567 PropertyChange our_interests;
1568 PropertyChange bounds;
1569 PropertyChange pos_and_length;
1570 bool save = false;
1572 if (in_set_state || in_flush) {
1573 return false;
1576 our_interests.add (Properties::muted);
1577 our_interests.add (Properties::layer);
1578 our_interests.add (Properties::opaque);
1580 bounds.add (Properties::start);
1581 bounds.add (Properties::position);
1582 bounds.add (Properties::length);
1584 pos_and_length.add (Properties::position);
1585 pos_and_length.add (Properties::length);
1587 if (what_changed.contains (bounds)) {
1588 region_bounds_changed (what_changed, region);
1589 save = !(_splicing || _nudging);
1592 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1593 check_dependents (region, false);
1596 if (what_changed.contains (Properties::position)) {
1597 notify_region_moved (region);
1601 /* don't notify about layer changes, since we are the only object that can initiate
1602 them, and we notify in ::relayer()
1605 if (what_changed.contains (our_interests)) {
1606 save = true;
1609 return save;
1612 void
1613 Playlist::drop_regions ()
1615 RegionLock rl (this);
1616 regions.clear ();
1617 all_regions.clear ();
1620 void
1621 Playlist::clear (bool with_signals)
1624 RegionLock rl (this);
1626 region_state_changed_connections.drop_connections ();
1628 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1629 pending_removes.insert (*i);
1632 regions.clear ();
1634 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1635 remove_dependents (*s);
1639 if (with_signals) {
1641 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1642 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1645 pending_removes.clear ();
1646 pending_length = false;
1647 LengthChanged ();
1648 pending_contents_change = false;
1649 ContentsChanged ();
1654 /***********************************************************************
1655 FINDING THINGS
1656 **********************************************************************/
1658 Playlist::RegionList *
1659 Playlist::regions_at (framepos_t frame)
1662 RegionLock rlock (this);
1663 return find_regions_at (frame);
1666 boost::shared_ptr<Region>
1667 Playlist::top_region_at (framepos_t frame)
1670 RegionLock rlock (this);
1671 RegionList *rlist = find_regions_at (frame);
1672 boost::shared_ptr<Region> region;
1674 if (rlist->size()) {
1675 RegionSortByLayer cmp;
1676 rlist->sort (cmp);
1677 region = rlist->back();
1680 delete rlist;
1681 return region;
1684 boost::shared_ptr<Region>
1685 Playlist::top_unmuted_region_at (framepos_t frame)
1688 RegionLock rlock (this);
1689 RegionList *rlist = find_regions_at (frame);
1691 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1693 RegionList::iterator tmp = i;
1694 ++tmp;
1696 if ((*i)->muted()) {
1697 rlist->erase (i);
1700 i = tmp;
1703 boost::shared_ptr<Region> region;
1705 if (rlist->size()) {
1706 RegionSortByLayer cmp;
1707 rlist->sort (cmp);
1708 region = rlist->back();
1711 delete rlist;
1712 return region;
1715 Playlist::RegionList*
1716 Playlist::regions_to_read (framepos_t start, framepos_t end)
1718 /* Caller must hold lock */
1720 RegionList covering;
1721 set<framepos_t> to_check;
1722 set<boost::shared_ptr<Region> > unique;
1724 to_check.insert (start);
1725 to_check.insert (end);
1727 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1729 /* find all/any regions that span start+end */
1731 switch ((*i)->coverage (start, end)) {
1732 case OverlapNone:
1733 break;
1735 case OverlapInternal:
1736 covering.push_back (*i);
1737 break;
1739 case OverlapStart:
1740 to_check.insert ((*i)->position());
1741 covering.push_back (*i);
1742 break;
1744 case OverlapEnd:
1745 to_check.insert ((*i)->last_frame());
1746 covering.push_back (*i);
1747 break;
1749 case OverlapExternal:
1750 covering.push_back (*i);
1751 to_check.insert ((*i)->position());
1752 to_check.insert ((*i)->last_frame());
1753 break;
1756 /* don't go too far */
1758 if ((*i)->position() > end) {
1759 break;
1763 RegionList* rlist = new RegionList;
1765 /* find all the regions that cover each position .... */
1767 if (covering.size() == 1) {
1769 rlist->push_back (covering.front());
1771 } else {
1773 RegionList here;
1774 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1776 here.clear ();
1778 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1780 if ((*x)->covers (*t)) {
1781 here.push_back (*x);
1785 RegionSortByLayer cmp;
1786 here.sort (cmp);
1788 /* ... and get the top/transparent regions at "here" */
1790 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1792 unique.insert (*c);
1794 if ((*c)->opaque()) {
1796 /* the other regions at this position are hidden by this one */
1798 break;
1803 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1804 rlist->push_back (*s);
1807 if (rlist->size() > 1) {
1808 /* now sort by time order */
1810 RegionSortByPosition cmp;
1811 rlist->sort (cmp);
1815 return rlist;
1818 Playlist::RegionList *
1819 Playlist::find_regions_at (framepos_t frame)
1821 /* Caller must hold lock */
1823 RegionList *rlist = new RegionList;
1825 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1826 if ((*i)->covers (frame)) {
1827 rlist->push_back (*i);
1831 return rlist;
1834 Playlist::RegionList *
1835 Playlist::regions_touched (framepos_t start, framepos_t end)
1837 RegionLock rlock (this);
1838 RegionList *rlist = new RegionList;
1840 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1841 if ((*i)->coverage (start, end) != OverlapNone) {
1842 rlist->push_back (*i);
1846 return rlist;
1849 framepos_t
1850 Playlist::find_next_transient (framepos_t from, int dir)
1852 RegionLock rlock (this);
1853 AnalysisFeatureList points;
1854 AnalysisFeatureList these_points;
1856 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1857 if (dir > 0) {
1858 if ((*i)->last_frame() < from) {
1859 continue;
1861 } else {
1862 if ((*i)->first_frame() > from) {
1863 continue;
1867 (*i)->get_transients (these_points);
1869 /* add first frame, just, err, because */
1871 these_points.push_back ((*i)->first_frame());
1873 points.insert (points.end(), these_points.begin(), these_points.end());
1874 these_points.clear ();
1877 if (points.empty()) {
1878 return -1;
1881 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1882 bool reached = false;
1884 if (dir > 0) {
1885 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1886 if ((*x) >= from) {
1887 reached = true;
1890 if (reached && (*x) > from) {
1891 return *x;
1894 } else {
1895 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1896 if ((*x) <= from) {
1897 reached = true;
1900 if (reached && (*x) < from) {
1901 return *x;
1906 return -1;
1909 boost::shared_ptr<Region>
1910 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1912 RegionLock rlock (this);
1913 boost::shared_ptr<Region> ret;
1914 framepos_t closest = max_frames;
1916 bool end_iter = false;
1918 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1920 if(end_iter) break;
1922 frameoffset_t distance;
1923 boost::shared_ptr<Region> r = (*i);
1924 framepos_t pos = 0;
1926 switch (point) {
1927 case Start:
1928 pos = r->first_frame ();
1929 break;
1930 case End:
1931 pos = r->last_frame ();
1932 break;
1933 case SyncPoint:
1934 pos = r->sync_position ();
1935 // r->adjust_to_sync (r->first_frame());
1936 break;
1939 switch (dir) {
1940 case 1: /* forwards */
1942 if (pos > frame) {
1943 if ((distance = pos - frame) < closest) {
1944 closest = distance;
1945 ret = r;
1946 end_iter = true;
1950 break;
1952 default: /* backwards */
1954 if (pos < frame) {
1955 if ((distance = frame - pos) < closest) {
1956 closest = distance;
1957 ret = r;
1960 else {
1961 end_iter = true;
1964 break;
1968 return ret;
1971 framepos_t
1972 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1974 RegionLock rlock (this);
1976 framepos_t closest = max_frames;
1977 framepos_t ret = -1;
1979 if (dir > 0) {
1981 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1983 boost::shared_ptr<Region> r = (*i);
1984 frameoffset_t distance;
1986 if (r->first_frame() > frame) {
1988 distance = r->first_frame() - frame;
1990 if (distance < closest) {
1991 ret = r->first_frame();
1992 closest = distance;
1996 if (r->last_frame () > frame) {
1998 distance = r->last_frame () - frame;
2000 if (distance < closest) {
2001 ret = r->last_frame ();
2002 closest = distance;
2007 } else {
2009 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2011 boost::shared_ptr<Region> r = (*i);
2012 frameoffset_t distance;
2014 if (r->last_frame() < frame) {
2016 distance = frame - r->last_frame();
2018 if (distance < closest) {
2019 ret = r->last_frame();
2020 closest = distance;
2024 if (r->first_frame() < frame) {
2026 distance = frame - r->first_frame();
2028 if (distance < closest) {
2029 ret = r->first_frame();
2030 closest = distance;
2036 return ret;
2039 /***********************************************************************/
2044 void
2045 Playlist::mark_session_dirty ()
2047 if (!in_set_state && !holding_state ()) {
2048 _session.set_dirty();
2052 bool
2053 Playlist::set_property (const PropertyBase& prop)
2055 if (prop == Properties::regions.property_id) {
2056 const RegionListProperty::ChangeRecord& change (dynamic_cast<const RegionListProperty*>(&prop)->change());
2057 regions.update (change);
2058 return (!change.added.empty() && !change.removed.empty());
2060 return false;
2063 void
2064 Playlist::rdiff (vector<StatefulDiffCommand*>& cmds) const
2066 RegionLock rlock (const_cast<Playlist *> (this));
2068 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2069 if ((*i)->changed ()) {
2070 StatefulDiffCommand* sdc = new StatefulDiffCommand (*i);
2071 cmds.push_back (sdc);
2076 void
2077 Playlist::clear_owned_history ()
2079 RegionLock rlock (this);
2081 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2082 (*i)->clear_history ();
2086 void
2087 Playlist::update (const RegionListProperty::ChangeRecord& change)
2089 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2090 name(), change.added.size(), change.removed.size()));
2092 freeze ();
2093 /* add the added regions */
2094 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2095 add_region ((*i), (*i)->position());
2097 /* remove the removed regions */
2098 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2099 remove_region (*i);
2102 thaw ();
2105 PropertyList*
2106 Playlist::property_factory (const XMLNode& history_node) const
2108 const XMLNodeList& children (history_node.children());
2109 PropertyList* prop_list = 0;
2111 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2113 if ((*i)->name() == capitalize (regions.property_name())) {
2115 RegionListProperty* rlp = new RegionListProperty (*const_cast<Playlist*> (this));
2117 if (rlp->load_history_state (**i)) {
2118 if (!prop_list) {
2119 prop_list = new PropertyList();
2121 prop_list->add (rlp);
2122 } else {
2123 delete rlp;
2128 return prop_list;
2132 Playlist::set_state (const XMLNode& node, int version)
2134 XMLNode *child;
2135 XMLNodeList nlist;
2136 XMLNodeConstIterator niter;
2137 XMLPropertyList plist;
2138 XMLPropertyConstIterator piter;
2139 XMLProperty *prop;
2140 boost::shared_ptr<Region> region;
2141 string region_name;
2143 in_set_state++;
2145 if (node.name() != "Playlist") {
2146 in_set_state--;
2147 return -1;
2150 freeze ();
2152 plist = node.properties();
2154 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2156 prop = *piter;
2158 if (prop->name() == X_("name")) {
2159 _name = prop->value();
2160 _set_sort_id ();
2161 } else if (prop->name() == X_("id")) {
2162 _id = prop->value();
2163 } else if (prop->name() == X_("orig_diskstream_id")) {
2164 _orig_diskstream_id = prop->value ();
2165 } else if (prop->name() == X_("frozen")) {
2166 _frozen = string_is_affirmative (prop->value());
2170 clear (true);
2172 nlist = node.children();
2174 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2176 child = *niter;
2178 if (child->name() == "Region") {
2180 if ((prop = child->property ("id")) == 0) {
2181 error << _("region state node has no ID, ignored") << endmsg;
2182 continue;
2185 ID id = prop->value ();
2187 if ((region = region_by_id (id))) {
2189 region->suspend_property_changes ();
2191 if (region->set_state (*child, version)) {
2192 region->resume_property_changes ();
2193 continue;
2196 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2197 region->suspend_property_changes ();
2198 } else {
2199 error << _("Playlist: cannot create region from XML") << endmsg;
2200 continue;
2203 add_region (region, region->position(), 1.0);
2205 // So that layer_op ordering doesn't get screwed up
2206 region->set_last_layer_op( region->layer());
2207 region->resume_property_changes ();
2211 /* update dependents, which was not done during add_region_internal
2212 due to in_set_state being true
2215 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2216 check_dependents (*r, false);
2219 thaw ();
2220 notify_contents_changed ();
2222 in_set_state--;
2223 first_set_state = false;
2224 return 0;
2227 XMLNode&
2228 Playlist::get_state()
2230 return state (true);
2233 XMLNode&
2234 Playlist::get_template()
2236 return state (false);
2239 /** @param full_state true to include regions in the returned state, otherwise false.
2241 XMLNode&
2242 Playlist::state (bool full_state)
2244 XMLNode *node = new XMLNode (X_("Playlist"));
2245 char buf[64];
2247 node->add_property (X_("id"), id().to_s());
2248 node->add_property (X_("name"), _name);
2249 node->add_property (X_("type"), _type.to_string());
2251 _orig_diskstream_id.print (buf, sizeof (buf));
2252 node->add_property (X_("orig_diskstream_id"), buf);
2253 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2255 if (full_state) {
2256 RegionLock rlock (this, false);
2257 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2258 node->add_child_nocopy ((*i)->get_state());
2262 if (_extra_xml) {
2263 node->add_child_copy (*_extra_xml);
2266 return *node;
2269 bool
2270 Playlist::empty() const
2272 RegionLock rlock (const_cast<Playlist *>(this), false);
2273 return regions.empty();
2276 uint32_t
2277 Playlist::n_regions() const
2279 RegionLock rlock (const_cast<Playlist *>(this), false);
2280 return regions.size();
2283 framecnt_t
2284 Playlist::get_maximum_extent () const
2286 RegionLock rlock (const_cast<Playlist *>(this), false);
2287 return _get_maximum_extent ();
2290 framecnt_t
2291 Playlist::_get_maximum_extent () const
2293 RegionList::const_iterator i;
2294 framecnt_t max_extent = 0;
2295 framepos_t end = 0;
2297 for (i = regions.begin(); i != regions.end(); ++i) {
2298 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
2299 max_extent = end;
2303 return max_extent;
2306 string
2307 Playlist::bump_name (string name, Session &session)
2309 string newname = name;
2311 do {
2312 newname = bump_name_once (newname);
2313 } while (session.playlists->by_name (newname)!=NULL);
2315 return newname;
2319 layer_t
2320 Playlist::top_layer() const
2322 RegionLock rlock (const_cast<Playlist *> (this));
2323 layer_t top = 0;
2325 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2326 top = max (top, (*i)->layer());
2328 return top;
2331 void
2332 Playlist::set_edit_mode (EditMode mode)
2334 _edit_mode = mode;
2337 /********************
2338 * Region Layering
2339 ********************/
2341 void
2342 Playlist::relayer ()
2344 /* never compute layers when changing state for undo/redo or setting from XML*/
2346 if (in_update || in_set_state) {
2347 return;
2350 bool changed = false;
2352 /* Build up a new list of regions on each layer, stored in a set of lists
2353 each of which represent some period of time on some layer. The idea
2354 is to avoid having to search the entire region list to establish whether
2355 each region overlaps another */
2357 /* how many pieces to divide this playlist's time up into */
2358 int const divisions = 512;
2360 /* find the start and end positions of the regions on this playlist */
2361 framepos_t start = UINT_MAX;
2362 framepos_t end = 0;
2363 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2364 start = min (start, (*i)->position());
2365 end = max (end, (*i)->position() + (*i)->length());
2368 /* hence the size of each time division */
2369 double const division_size = (end - start) / double (divisions);
2371 vector<vector<RegionList> > layers;
2372 layers.push_back (vector<RegionList> (divisions));
2374 /* we want to go through regions from desired lowest to desired highest layer,
2375 which depends on the layer model
2378 RegionList copy = regions.rlist();
2380 /* sort according to the model and the layering mode that we're in */
2382 if (_explicit_relayering) {
2384 copy.sort (RegionSortByLayerWithPending ());
2386 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2388 copy.sort (RegionSortByLastLayerOp ());
2393 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2395 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2396 (*i)->set_pending_explicit_relayer (false);
2398 /* find the time divisions that this region covers */
2399 int const start_division = floor ( ((*i)->position() - start) / division_size);
2400 int end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2401 if (end_division == divisions) {
2402 end_division--;
2405 assert (end_division < divisions);
2407 /* find the lowest layer that this region can go on */
2408 size_t j = layers.size();
2409 while (j > 0) {
2410 /* try layer j - 1; it can go on if it overlaps no other region
2411 that is already on that layer
2414 bool overlap = false;
2415 for (int k = start_division; k <= end_division; ++k) {
2416 RegionList::iterator l = layers[j-1][k].begin ();
2417 while (l != layers[j-1][k].end()) {
2418 if ((*l)->overlap_equivalent (*i)) {
2419 overlap = true;
2420 break;
2422 l++;
2425 if (overlap) {
2426 break;
2430 if (overlap) {
2431 /* overlap, so we must use layer j */
2432 break;
2435 --j;
2438 if (j == layers.size()) {
2439 /* we need a new layer for this region */
2440 layers.push_back (vector<RegionList> (divisions));
2443 /* put a reference to this region in each of the divisions that it exists in */
2444 for (int k = start_division; k <= end_division; ++k) {
2445 layers[j][k].push_back (*i);
2448 if ((*i)->layer() != j) {
2449 changed = true;
2452 (*i)->set_layer (j);
2455 if (changed) {
2456 notify_layering_changed ();
2460 /* XXX these layer functions are all deprecated */
2462 void
2463 Playlist::raise_region (boost::shared_ptr<Region> region)
2465 uint32_t rsz = regions.size();
2466 layer_t target = region->layer() + 1U;
2468 if (target >= rsz) {
2469 /* its already at the effective top */
2470 return;
2473 move_region_to_layer (target, region, 1);
2476 void
2477 Playlist::lower_region (boost::shared_ptr<Region> region)
2479 if (region->layer() == 0) {
2480 /* its already at the bottom */
2481 return;
2484 layer_t target = region->layer() - 1U;
2486 move_region_to_layer (target, region, -1);
2489 void
2490 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2492 /* does nothing useful if layering mode is later=higher */
2493 if ((_session.config.get_layer_model() == MoveAddHigher) ||
2494 (_session.config.get_layer_model() == AddHigher)) {
2495 timestamp_layer_op (region);
2496 relayer ();
2500 void
2501 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2503 /* does nothing useful if layering mode is later=higher */
2504 if ((_session.config.get_layer_model() == MoveAddHigher) ||
2505 (_session.config.get_layer_model() == AddHigher)) {
2506 region->set_last_layer_op (0);
2507 relayer ();
2512 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2514 RegionList::iterator i;
2515 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2516 list<LayerInfo> layerinfo;
2519 RegionLock rlock (const_cast<Playlist *> (this));
2521 for (i = regions.begin(); i != regions.end(); ++i) {
2523 if (region == *i) {
2524 continue;
2527 layer_t dest;
2529 if (dir > 0) {
2531 /* region is moving up, move all regions on intermediate layers
2532 down 1
2535 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2536 dest = (*i)->layer() - 1;
2537 } else {
2538 /* not affected */
2539 continue;
2541 } else {
2543 /* region is moving down, move all regions on intermediate layers
2544 up 1
2547 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2548 dest = (*i)->layer() + 1;
2549 } else {
2550 /* not affected */
2551 continue;
2555 LayerInfo newpair;
2557 newpair.first = *i;
2558 newpair.second = dest;
2560 layerinfo.push_back (newpair);
2564 /* now reset the layers without holding the region lock */
2566 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2567 x->first->set_layer (x->second);
2570 region->set_layer (target_layer);
2572 #if 0
2573 /* now check all dependents */
2575 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2576 check_dependents (x->first, false);
2579 check_dependents (region, false);
2580 #endif
2582 return 0;
2585 void
2586 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2588 RegionList::iterator i;
2589 bool moved = false;
2591 _nudging = true;
2594 RegionLock rlock (const_cast<Playlist *> (this));
2596 for (i = regions.begin(); i != regions.end(); ++i) {
2598 if ((*i)->position() >= start) {
2600 framepos_t new_pos;
2602 if (forwards) {
2604 if ((*i)->last_frame() > max_frames - distance) {
2605 new_pos = max_frames - (*i)->length();
2606 } else {
2607 new_pos = (*i)->position() + distance;
2610 } else {
2612 if ((*i)->position() > distance) {
2613 new_pos = (*i)->position() - distance;
2614 } else {
2615 new_pos = 0;
2619 (*i)->set_position (new_pos, this);
2620 moved = true;
2625 if (moved) {
2626 _nudging = false;
2627 notify_length_changed ();
2632 boost::shared_ptr<Region>
2633 Playlist::find_region (const ID& id) const
2635 RegionLock rlock (const_cast<Playlist*> (this));
2637 /* searches all regions currently in use by the playlist */
2639 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2640 if ((*i)->id() == id) {
2641 return *i;
2645 return boost::shared_ptr<Region> ();
2648 boost::shared_ptr<Region>
2649 Playlist::region_by_id (const ID& id)
2651 /* searches all regions ever added to this playlist */
2653 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2654 if ((*i)->id() == id) {
2655 return *i;
2658 return boost::shared_ptr<Region> ();
2661 void
2662 Playlist::dump () const
2664 boost::shared_ptr<Region> r;
2666 cerr << "Playlist \"" << _name << "\" " << endl
2667 << regions.size() << " regions "
2668 << endl;
2670 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2671 r = *i;
2672 cerr << " " << r->name() << " ["
2673 << r->start() << "+" << r->length()
2674 << "] at "
2675 << r->position()
2676 << " on layer "
2677 << r->layer ()
2678 << endl;
2682 void
2683 Playlist::set_frozen (bool yn)
2685 _frozen = yn;
2688 void
2689 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2691 // struct timeval tv;
2692 // gettimeofday (&tv, 0);
2693 region->set_last_layer_op (++layer_op_counter);
2697 void
2698 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2700 bool moved = false;
2702 if (region->locked()) {
2703 return;
2706 _shuffling = true;
2709 RegionLock rlock (const_cast<Playlist*> (this));
2712 if (dir > 0) {
2714 RegionList::iterator next;
2716 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2717 if ((*i) == region) {
2718 next = i;
2719 ++next;
2721 if (next != regions.end()) {
2723 if ((*next)->locked()) {
2724 break;
2727 framepos_t new_pos;
2729 if ((*next)->position() != region->last_frame() + 1) {
2730 /* they didn't used to touch, so after shuffle,
2731 just have them swap positions.
2733 new_pos = (*next)->position();
2734 } else {
2735 /* they used to touch, so after shuffle,
2736 make sure they still do. put the earlier
2737 region where the later one will end after
2738 it is moved.
2740 new_pos = region->position() + (*next)->length();
2743 (*next)->set_position (region->position(), this);
2744 region->set_position (new_pos, this);
2746 /* avoid a full sort */
2748 regions.erase (i); // removes the region from the list */
2749 next++;
2750 regions.insert (next, region); // adds it back after next
2752 moved = true;
2754 break;
2757 } else {
2759 RegionList::iterator prev = regions.end();
2761 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2762 if ((*i) == region) {
2764 if (prev != regions.end()) {
2766 if ((*prev)->locked()) {
2767 break;
2770 framepos_t new_pos;
2771 if (region->position() != (*prev)->last_frame() + 1) {
2772 /* they didn't used to touch, so after shuffle,
2773 just have them swap positions.
2775 new_pos = region->position();
2776 } else {
2777 /* they used to touch, so after shuffle,
2778 make sure they still do. put the earlier
2779 one where the later one will end after
2781 new_pos = (*prev)->position() + region->length();
2784 region->set_position ((*prev)->position(), this);
2785 (*prev)->set_position (new_pos, this);
2787 /* avoid a full sort */
2789 regions.erase (i); // remove region
2790 regions.insert (prev, region); // insert region before prev
2792 moved = true;
2795 break;
2801 _shuffling = false;
2803 if (moved) {
2805 relayer ();
2806 check_dependents (region, false);
2808 notify_contents_changed();
2813 bool
2814 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2816 RegionLock rlock (const_cast<Playlist*> (this));
2818 if (regions.size() > 1) {
2819 return true;
2822 return false;
2825 void
2826 Playlist::update_after_tempo_map_change ()
2828 RegionLock rlock (const_cast<Playlist*> (this));
2829 RegionList copy (regions.rlist());
2831 freeze ();
2833 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2834 (*i)->update_position_after_tempo_map_change ();
2837 thaw ();
2840 void
2841 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2843 RegionLock rl (this, false);
2844 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2845 s (*i);
2849 void
2850 Playlist::set_explicit_relayering (bool e)
2852 if (e == false && _explicit_relayering == true) {
2854 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2855 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2856 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2857 at this point would keep regions on the same layers.
2859 From then on in, it's just you and your towel.
2862 RegionLock rl (this);
2863 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2864 (*i)->set_last_layer_op ((*i)->layer ());
2868 _explicit_relayering = e;
2872 bool
2873 Playlist::has_region_at (framepos_t const p) const
2875 RegionLock (const_cast<Playlist *> (this));
2877 RegionList::const_iterator i = regions.begin ();
2878 while (i != regions.end() && !(*i)->covers (p)) {
2879 ++i;
2882 return (i != regions.end());