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.
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"
46 using 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
) {}
58 cerr
<< ">>>>" << name
<< endl
; playlist
->dump(); cerr
<< "<<<<" << name
<< endl
<< endl
;
60 boost::shared_ptr
<Playlist
> playlist
;
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()) {
78 double q
= b
->layer ();
79 if (b
->pending_explicit_relayer()) {
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();
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
))
112 boost::shared_ptr
<Region
>
113 RegionListProperty::lookup_id (const ID
& id
)
115 boost::shared_ptr
<Region
> ret
= _playlist
.region_by_id (id
);
118 ret
= RegionFactory::region_by_id (id
);
125 RegionListProperty::copy_for_history () const
127 RegionListProperty
* copy
= new RegionListProperty (_playlist
);
128 /* this is all we need */
129 copy
->_change
= _change
;
134 RegionListProperty::diff (PropertyList
& undo
, PropertyList
& redo
) const
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 ();
149 Playlist::Playlist (Session
& sess
, string nom
, DataType type
, bool hide
)
150 : SessionObject(sess
, nom
)
155 first_set_state
= false;
161 Playlist::Playlist (Session
& sess
, const XMLNode
& node
, DataType type
, bool hide
)
162 : SessionObject(sess
, "unnamed playlist")
167 const XMLProperty
* prop
= node
.property("type");
168 assert(!prop
|| DataType(prop
->value()) == _type
);
171 _name
= "unnamed"; /* reset by set_state */
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
)
180 , _type(other
->_type
)
181 , _orig_diskstream_id(other
->_orig_diskstream_id
)
186 other
->copy_regions (tmp
);
190 for (list
<boost::shared_ptr
<Region
> >::iterator x
= tmp
.begin(); x
!= tmp
.end(); ++x
) {
191 add_region_internal( (*x
), (*x
)->position());
196 _splicing
= other
->_splicing
;
197 _nudging
= other
->_nudging
;
198 _edit_mode
= other
->_edit_mode
;
201 first_set_state
= false;
203 in_partition
= false;
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
)
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;
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;
238 overlap
= region
->coverage (start
, end
);
244 case OverlapInternal
:
245 offset
= start
- region
->position();
252 position
= region
->position() - start
;
253 len
= end
- region
->position();
257 offset
= start
- region
->position();
259 len
= region
->length() - offset
;
262 case OverlapExternal
:
264 position
= region
->position() - start
;
265 len
= region
->length();
269 RegionFactory::region_name (new_name
, region
->name(), false);
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
);
284 first_set_state
= false;
286 /* this constructor does NOT notify others (session) */
293 InUse (true); /* EMIT SIGNAL */
304 InUse (false); /* EMIT SIGNAL */
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
));
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;
337 _edit_mode
= Config
->get_edit_mode();
339 in_partition
= false;
341 _read_data_count
= 0;
343 layer_op_counter
= 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 */
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
) {
382 string t
= _name
.val().substr(dot_position
+ 1);
385 _sort_id
= boost::lexical_cast
<int>(t
);
388 catch (boost::bad_lexical_cast e
) {
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.
407 bool ret
= SessionObject::set_name(str
);
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 ***********************************************************************/
423 Playlist::begin_undo ()
430 Playlist::end_undo ()
439 delay_notifications ();
440 g_atomic_int_inc (&ignore_state_changes
);
446 g_atomic_int_dec_and_test (&ignore_state_changes
);
447 release_notifications ();
452 Playlist::delay_notifications ()
454 g_atomic_int_inc (&block_notifications
);
455 freeze_length
= _get_maximum_extent();
459 Playlist::release_notifications ()
461 if (g_atomic_int_dec_and_test (&block_notifications
)) {
462 flush_notifications ();
468 Playlist::notify_contents_changed ()
470 if (holding_state ()) {
471 pending_contents_change
= true;
473 pending_contents_change
= false;
474 ContentsChanged(); /* EMIT SIGNAL */
479 Playlist::notify_layering_changed ()
481 if (holding_state ()) {
482 pending_layering
= true;
484 pending_layering
= false;
485 LayeringChanged(); /* EMIT SIGNAL */
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;
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 */
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
);
519 list
< Evoral::RangeMove
<framepos_t
> > m
;
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;
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 */
547 Playlist::notify_length_changed ()
549 if (holding_state ()) {
550 pending_length
= true;
552 pending_length
= false;
553 LengthChanged(); /* EMIT SIGNAL */
554 pending_contents_change
= false;
555 ContentsChanged (); /* EMIT SIGNAL */
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;
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 ();
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
);
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
) {
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
);
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 /*************************************************************
660 *************************************************************/
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);
677 add_region_internal (region
, pos
);
678 pos
+= region
->length();
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
)));
698 RegionFactory::region_name (name
, region
->name(), false);
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
>());
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
);
729 Playlist::add_region_internal (boost::shared_ptr
<Region
> region
, framepos_t position
)
731 if (region
->data_type() != _type
){
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 */
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
)));
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
;
788 remove_region_internal (old
);
789 add_region_internal (newr
, pos
);
793 possibly_splice_unlocked (pos
, old
->length() - newr
->length());
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;
810 if (!holding_state()) {
811 old_length
= _get_maximum_extent();
816 region
->set_playlist (boost::weak_ptr
<Playlist
>());
819 /* XXX should probably freeze here .... */
821 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
824 framepos_t pos
= (*i
)->position();
825 framecnt_t distance
= (*i
)->length();
829 possibly_splice_unlocked (pos
, -distance
);
831 if (!holding_state ()) {
833 remove_dependents (region
);
835 if (old_length
!= _get_maximum_extent()) {
836 notify_length_changed ();
840 notify_region_removed (region
);
846 /* XXX and thaw ... */
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
));
861 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
862 if ((*i
)->equivalent (other
)) {
863 results
.push_back ((*i
));
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
);
881 Playlist::partition (framepos_t start
, framepos_t end
, bool cut
)
885 partition_internal (start
, end
, cut
, thawlist
);
887 for (RegionList::iterator i
= thawlist
.begin(); i
!= thawlist
.end(); ++i
) {
888 (*i
)->resume_property_changes ();
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
;
903 RegionList::iterator tmp
;
905 framepos_t pos1
, pos2
, pos3
, pos4
;
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
) {
922 if (current
->first_frame() >= start
&& current
->last_frame() < end
) {
925 remove_region_internal (current
);
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
) {
940 if ((overlap
= current
->coverage (start
, end
)) == OverlapNone
) {
944 pos1
= current
->position();
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.
956 ---------------*************************------------
959 ---------------*****++++++++++++++++====------------
961 ---------------*****----------------====------------
966 /* "middle" ++++++ */
968 RegionFactory::region_name (new_name
, current
->name(), false);
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
);
987 RegionFactory::region_name (new_name
, current
->name(), false);
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
);
1005 current
->suspend_property_changes ();
1006 thawlist
.push_back (current
);
1007 current
->trim_end (pos2
, this);
1009 } else if (overlap
== OverlapEnd
) {
1013 ---------------*************************------------
1016 ---------------**************+++++++++++------------
1018 ---------------**************-----------------------
1025 RegionFactory::region_name (new_name
, current
->name(), false);
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
);
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
1056 ---------------*************************------------
1060 ---------------****+++++++++++++++++++++------------
1062 -------------------*********************------------
1068 RegionFactory::region_name (new_name
, current
->name(), false);
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
);
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.
1098 ---------------*************************------------
1102 ---------------*************************------------
1104 ----------------------------------------------------
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
;
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()) {
1145 /* paste the next section into the nascent playlist,
1146 offset to reflect the start of the first range we
1150 ret
->paste (pl
, (*i
).start
- start
, 1.0f
);
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
;
1178 snprintf (buf
, sizeof (buf
), "%" PRIu32
, ++subcnt
);
1179 string new_name
= _name
;
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();
1196 boost::shared_ptr
<Playlist
>
1197 Playlist::copy (framepos_t start
, framecnt_t cnt
, bool result_is_hidden
)
1201 snprintf (buf
, sizeof (buf
), "%" PRIu32
, ++subcnt
);
1202 string new_name
= _name
;
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();
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
);
1241 /* XXX shall we handle fractional cases at some point? */
1243 if (old_length
!= _get_maximum_extent()) {
1244 notify_length_changed ();
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
;
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
)));
1272 RegionFactory::region_name (name
, region
->name(), false);
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
);
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());
1294 for (RegionList::iterator r
= copy
.begin(); r
!= copy
.end(); ++r
) {
1296 if ((*r
)->last_frame() < at
) {
1301 if (at
> (*r
)->first_frame() && at
< (*r
)->last_frame()) {
1302 /* intersected region */
1303 if (!move_intersected
) {
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
);
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 ();
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
);
1340 Playlist::split_region (boost::shared_ptr
<Region
> region
, framepos_t playlist_position
)
1342 RegionLock
rl (this);
1343 _split_region (region
, playlist_position
);
1347 Playlist::_split_region (boost::shared_ptr
<Region
> region
, framepos_t playlist_position
)
1349 if (!region
->covers (playlist_position
)) {
1353 if (region
->position() == playlist_position
||
1354 region
->last_frame() == playlist_position
) {
1358 boost::shared_ptr
<Region
> left
;
1359 boost::shared_ptr
<Region
> right
;
1360 frameoffset_t before
;
1361 frameoffset_t after
;
1365 /* split doesn't change anything about length, so don't try to splice */
1367 bool old_sp
= _splicing
;
1370 before
= playlist_position
- region
->position();
1371 after
= region
->length() - before
;
1373 RegionFactory::region_name (before_name
, region
->name(), false);
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);
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);
1414 finalize_split_region (region
, left
, right
);
1416 remove_region_internal (region
);
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 */
1429 if (_edit_mode
== Splice
) {
1430 splice_locked (at
, distance
, exclude
);
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 */
1442 if (_edit_mode
== Splice
) {
1443 splice_unlocked (at
, distance
, exclude
);
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
);
1457 Playlist::splice_unlocked (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1459 core_splice (at
, distance
, exclude
);
1463 Playlist::core_splice (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1467 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1469 if (exclude
&& (*i
) == exclude
) {
1473 if ((*i
)->position() >= at
) {
1474 framepos_t new_pos
= (*i
)->position() + distance
;
1477 } else if (new_pos
>= max_frames
- (*i
)->length()) {
1478 new_pos
= max_frames
- (*i
)->length();
1481 (*i
)->set_position (new_pos
, this);
1487 notify_length_changed ();
1491 Playlist::region_bounds_changed (const PropertyChange
& what_changed
, boost::shared_ptr
<Region
> region
)
1493 if (in_set_state
|| _splicing
|| _nudging
|| _shuffling
) {
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
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();
1532 possibly_splice (region
->last_position() + region
->last_length(), delta
, region
);
1535 if (holding_state ()) {
1536 pending_bounds
.push_back (region
);
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 ();
1545 check_dependents (region
, false);
1551 Playlist::region_changed_proxy (const PropertyChange
& what_changed
, boost::weak_ptr
<Region
> weak_region
)
1553 boost::shared_ptr
<Region
> region (weak_region
.lock());
1559 /* this makes a virtual call to the right kind of playlist ... */
1561 region_changed (what_changed
, region
);
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
;
1572 if (in_set_state
|| in_flush
) {
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
)) {
1613 Playlist::drop_regions ()
1615 RegionLock
rl (this);
1617 all_regions
.clear ();
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
);
1634 for (set
<boost::shared_ptr
<Region
> >::iterator s
= pending_removes
.begin(); s
!= pending_removes
.end(); ++s
) {
1635 remove_dependents (*s
);
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;
1648 pending_contents_change
= false;
1654 /***********************************************************************
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
;
1677 region
= rlist
->back();
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
;
1696 if ((*i
)->muted()) {
1703 boost::shared_ptr
<Region
> region
;
1705 if (rlist
->size()) {
1706 RegionSortByLayer cmp
;
1708 region
= rlist
->back();
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
)) {
1735 case OverlapInternal
:
1736 covering
.push_back (*i
);
1740 to_check
.insert ((*i
)->position());
1741 covering
.push_back (*i
);
1745 to_check
.insert ((*i
)->last_frame());
1746 covering
.push_back (*i
);
1749 case OverlapExternal
:
1750 covering
.push_back (*i
);
1751 to_check
.insert ((*i
)->position());
1752 to_check
.insert ((*i
)->last_frame());
1756 /* don't go too far */
1758 if ((*i
)->position() > end
) {
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());
1774 for (set
<framepos_t
>::iterator t
= to_check
.begin(); t
!= to_check
.end(); ++t
) {
1778 for (RegionList::iterator x
= covering
.begin(); x
!= covering
.end(); ++x
) {
1780 if ((*x
)->covers (*t
)) {
1781 here
.push_back (*x
);
1785 RegionSortByLayer cmp
;
1788 /* ... and get the top/transparent regions at "here" */
1790 for (RegionList::reverse_iterator c
= here
.rbegin(); c
!= here
.rend(); ++c
) {
1794 if ((*c
)->opaque()) {
1796 /* the other regions at this position are hidden by this one */
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
;
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
);
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
);
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
) {
1858 if ((*i
)->last_frame() < from
) {
1862 if ((*i
)->first_frame() > from
) {
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()) {
1881 TransientDetector::cleanup_transients (points
, _session
.frame_rate(), 3.0);
1882 bool reached
= false;
1885 for (AnalysisFeatureList::iterator x
= points
.begin(); x
!= points
.end(); ++x
) {
1890 if (reached
&& (*x
) > from
) {
1895 for (AnalysisFeatureList::reverse_iterator x
= points
.rbegin(); x
!= points
.rend(); ++x
) {
1900 if (reached
&& (*x
) < from
) {
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
) {
1922 frameoffset_t distance
;
1923 boost::shared_ptr
<Region
> r
= (*i
);
1928 pos
= r
->first_frame ();
1931 pos
= r
->last_frame ();
1934 pos
= r
->sync_position ();
1935 // r->adjust_to_sync (r->first_frame());
1940 case 1: /* forwards */
1943 if ((distance
= pos
- frame
) < closest
) {
1952 default: /* backwards */
1955 if ((distance
= frame
- pos
) < closest
) {
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;
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();
1996 if (r
->last_frame () > frame
) {
1998 distance
= r
->last_frame () - frame
;
2000 if (distance
< closest
) {
2001 ret
= r
->last_frame ();
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();
2024 if (r
->first_frame() < frame
) {
2026 distance
= frame
- r
->first_frame();
2028 if (distance
< closest
) {
2029 ret
= r
->first_frame();
2039 /***********************************************************************/
2045 Playlist::mark_session_dirty ()
2047 if (!in_set_state
&& !holding_state ()) {
2048 _session
.set_dirty();
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());
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
);
2077 Playlist::clear_owned_history ()
2079 RegionLock
rlock (this);
2081 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2082 (*i
)->clear_history ();
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()));
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
) {
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
)) {
2119 prop_list
= new PropertyList();
2121 prop_list
->add (rlp
);
2132 Playlist::set_state (const XMLNode
& node
, int version
)
2136 XMLNodeConstIterator niter
;
2137 XMLPropertyList plist
;
2138 XMLPropertyConstIterator piter
;
2140 boost::shared_ptr
<Region
> region
;
2145 if (node
.name() != "Playlist") {
2152 plist
= node
.properties();
2154 for (piter
= plist
.begin(); piter
!= plist
.end(); ++piter
) {
2158 if (prop
->name() == X_("name")) {
2159 _name
= prop
->value();
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());
2172 nlist
= node
.children();
2174 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
2178 if (child
->name() == "Region") {
2180 if ((prop
= child
->property ("id")) == 0) {
2181 error
<< _("region state node has no ID, ignored") << endmsg
;
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 ();
2196 } else if ((region
= RegionFactory::create (_session
, *child
, true)) != 0) {
2197 region
->suspend_property_changes ();
2199 error
<< _("Playlist: cannot create region from XML") << endmsg
;
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);
2220 notify_contents_changed ();
2223 first_set_state
= false;
2228 Playlist::get_state()
2230 return state (true);
2234 Playlist::get_template()
2236 return state (false);
2239 /** @param full_state true to include regions in the returned state, otherwise false.
2242 Playlist::state (bool full_state
)
2244 XMLNode
*node
= new XMLNode (X_("Playlist"));
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");
2256 RegionLock
rlock (this, false);
2257 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2258 node
->add_child_nocopy ((*i
)->get_state());
2263 node
->add_child_copy (*_extra_xml
);
2270 Playlist::empty() const
2272 RegionLock
rlock (const_cast<Playlist
*>(this), false);
2273 return regions
.empty();
2277 Playlist::n_regions() const
2279 RegionLock
rlock (const_cast<Playlist
*>(this), false);
2280 return regions
.size();
2284 Playlist::get_maximum_extent () const
2286 RegionLock
rlock (const_cast<Playlist
*>(this), false);
2287 return _get_maximum_extent ();
2291 Playlist::_get_maximum_extent () const
2293 RegionList::const_iterator i
;
2294 framecnt_t max_extent
= 0;
2297 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2298 if ((end
= (*i
)->position() + (*i
)->length()) > max_extent
) {
2307 Playlist::bump_name (string name
, Session
&session
)
2309 string newname
= name
;
2312 newname
= bump_name_once (newname
);
2313 } while (session
.playlists
->by_name (newname
)!=NULL
);
2320 Playlist::top_layer() const
2322 RegionLock
rlock (const_cast<Playlist
*> (this));
2325 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2326 top
= max (top
, (*i
)->layer());
2332 Playlist::set_edit_mode (EditMode mode
)
2337 /********************
2339 ********************/
2342 Playlist::relayer ()
2344 /* never compute layers when changing state for undo/redo or setting from XML*/
2346 if (in_update
|| in_set_state
) {
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
;
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
) {
2405 assert (end_division
< divisions
);
2407 /* find the lowest layer that this region can go on */
2408 size_t j
= layers
.size();
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
)) {
2431 /* overlap, so we must use layer 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
) {
2452 (*i
)->set_layer (j
);
2456 notify_layering_changed ();
2460 /* XXX these layer functions are all deprecated */
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 */
2473 move_region_to_layer (target
, region
, 1);
2477 Playlist::lower_region (boost::shared_ptr
<Region
> region
)
2479 if (region
->layer() == 0) {
2480 /* its already at the bottom */
2484 layer_t target
= region
->layer() - 1U;
2486 move_region_to_layer (target
, region
, -1);
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
);
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);
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
) {
2531 /* region is moving up, move all regions on intermediate layers
2535 if ((*i
)->layer() > region
->layer() && (*i
)->layer() <= target_layer
) {
2536 dest
= (*i
)->layer() - 1;
2543 /* region is moving down, move all regions on intermediate layers
2547 if ((*i
)->layer() < region
->layer() && (*i
)->layer() >= target_layer
) {
2548 dest
= (*i
)->layer() + 1;
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
);
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);
2586 Playlist::nudge_after (framepos_t start
, framecnt_t distance
, bool forwards
)
2588 RegionList::iterator i
;
2594 RegionLock
rlock (const_cast<Playlist
*> (this));
2596 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2598 if ((*i
)->position() >= start
) {
2604 if ((*i
)->last_frame() > max_frames
- distance
) {
2605 new_pos
= max_frames
- (*i
)->length();
2607 new_pos
= (*i
)->position() + distance
;
2612 if ((*i
)->position() > distance
) {
2613 new_pos
= (*i
)->position() - distance
;
2619 (*i
)->set_position (new_pos
, this);
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
) {
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
) {
2658 return boost::shared_ptr
<Region
> ();
2662 Playlist::dump () const
2664 boost::shared_ptr
<Region
> r
;
2666 cerr
<< "Playlist \"" << _name
<< "\" " << endl
2667 << regions
.size() << " regions "
2670 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2672 cerr
<< " " << r
->name() << " ["
2673 << r
->start() << "+" << r
->length()
2683 Playlist::set_frozen (bool yn
)
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
);
2698 Playlist::shuffle (boost::shared_ptr
<Region
> region
, int dir
)
2702 if (region
->locked()) {
2709 RegionLock
rlock (const_cast<Playlist
*> (this));
2714 RegionList::iterator next
;
2716 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2717 if ((*i
) == region
) {
2721 if (next
!= regions
.end()) {
2723 if ((*next
)->locked()) {
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();
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
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 */
2750 regions
.insert (next
, region
); // adds it back after next
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()) {
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();
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
2806 check_dependents (region
, false);
2808 notify_contents_changed();
2814 Playlist::region_is_shuffle_constrained (boost::shared_ptr
<Region
>)
2816 RegionLock
rlock (const_cast<Playlist
*> (this));
2818 if (regions
.size() > 1) {
2826 Playlist::update_after_tempo_map_change ()
2828 RegionLock
rlock (const_cast<Playlist
*> (this));
2829 RegionList
copy (regions
.rlist());
2833 for (RegionList::iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
2834 (*i
)->update_position_after_tempo_map_change ();
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
) {
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
;
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
)) {
2882 return (i
!= regions
.end());