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.
29 #include <boost/lexical_cast.hpp>
31 #include "pbd/failed_constructor.h"
32 #include "pbd/stateful_diff_command.h"
33 #include "pbd/xml++.h"
34 #include "pbd/stacktrace.h"
36 #include "ardour/debug.h"
37 #include "ardour/playlist.h"
38 #include "ardour/session.h"
39 #include "ardour/region.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/playlist_factory.h"
42 #include "ardour/transient_detector.h"
43 #include "ardour/session_playlists.h"
44 #include "ardour/source_factory.h"
49 using namespace ARDOUR
;
53 namespace Properties
{
54 PBD::PropertyDescriptor
<bool> regions
;
58 struct ShowMeTheList
{
59 ShowMeTheList (boost::shared_ptr
<Playlist
> pl
, const string
& n
) : playlist (pl
), name (n
) {}
61 cerr
<< ">>>>" << name
<< endl
; playlist
->dump(); cerr
<< "<<<<" << name
<< endl
<< endl
;
63 boost::shared_ptr
<Playlist
> playlist
;
67 struct RegionSortByLayer
{
68 bool operator() (boost::shared_ptr
<Region
> a
, boost::shared_ptr
<Region
> b
) {
69 return a
->layer() < b
->layer();
73 struct RegionSortByLayerWithPending
{
74 bool operator () (boost::shared_ptr
<Region
> a
, boost::shared_ptr
<Region
> b
) {
76 double p
= a
->layer ();
77 if (a
->pending_explicit_relayer()) {
81 double q
= b
->layer ();
82 if (b
->pending_explicit_relayer()) {
90 struct RegionSortByPosition
{
91 bool operator() (boost::shared_ptr
<Region
> a
, boost::shared_ptr
<Region
> b
) {
92 return a
->position() < b
->position();
96 struct RegionSortByLastLayerOp
{
97 bool operator() (boost::shared_ptr
<Region
> a
, boost::shared_ptr
<Region
> b
) {
98 return a
->last_layer_op() < b
->last_layer_op();
103 Playlist::make_property_quarks ()
105 Properties::regions
.property_id
= g_quark_from_static_string (X_("regions"));
106 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for regions = %1\n",
107 Properties::regions
.property_id
));
110 RegionListProperty::RegionListProperty (Playlist
& pl
)
111 : SequenceProperty
<std::list
<boost::shared_ptr
<Region
> > > (Properties::regions
.property_id
, boost::bind (&Playlist::update
, &pl
, _1
))
117 RegionListProperty::RegionListProperty (RegionListProperty
const & p
)
118 : PBD::SequenceProperty
<std::list
<boost::shared_ptr
<Region
> > > (p
)
119 , _playlist (p
._playlist
)
125 RegionListProperty::clone () const
127 return new RegionListProperty (*this);
131 RegionListProperty::create () const
133 return new RegionListProperty (_playlist
);
137 RegionListProperty::get_content_as_xml (boost::shared_ptr
<Region
> region
, XMLNode
& node
) const
139 /* All regions (even those which are deleted) have their state saved by other
140 code, so we can just store ID here.
143 node
.add_property ("id", region
->id().to_s ());
146 boost::shared_ptr
<Region
>
147 RegionListProperty::get_content_from_xml (XMLNode
const & node
) const
149 XMLProperty
const * prop
= node
.property ("id");
152 PBD::ID
id (prop
->value ());
154 boost::shared_ptr
<Region
> ret
= _playlist
.region_by_id (id
);
157 ret
= RegionFactory::region_by_id (id
);
163 Playlist::Playlist (Session
& sess
, string nom
, DataType type
, bool hide
)
164 : SessionObject(sess
, nom
)
169 first_set_state
= false;
174 Playlist::Playlist (Session
& sess
, const XMLNode
& node
, DataType type
, bool hide
)
175 : SessionObject(sess
, "unnamed playlist")
180 const XMLProperty
* prop
= node
.property("type");
181 assert(!prop
|| DataType(prop
->value()) == _type
);
185 _name
= "unnamed"; /* reset by set_state */
188 /* set state called by derived class */
191 Playlist::Playlist (boost::shared_ptr
<const Playlist
> other
, string namestr
, bool hide
)
192 : SessionObject(other
->_session
, namestr
)
194 , _type(other
->_type
)
195 , _orig_diskstream_id (other
->_orig_diskstream_id
)
200 other
->copy_regions (tmp
);
204 for (list
<boost::shared_ptr
<Region
> >::iterator x
= tmp
.begin(); x
!= tmp
.end(); ++x
) {
205 add_region_internal( (*x
), (*x
)->position());
210 _splicing
= other
->_splicing
;
211 _nudging
= other
->_nudging
;
212 _edit_mode
= other
->_edit_mode
;
215 first_set_state
= false;
217 in_partition
= false;
219 _read_data_count
= 0;
220 _frozen
= other
->_frozen
;
222 layer_op_counter
= other
->layer_op_counter
;
223 freeze_length
= other
->freeze_length
;
226 Playlist::Playlist (boost::shared_ptr
<const Playlist
> other
, framepos_t start
, framecnt_t cnt
, string str
, bool hide
)
227 : SessionObject(other
->_session
, str
)
229 , _type(other
->_type
)
230 , _orig_diskstream_id (other
->_orig_diskstream_id
)
232 RegionLock
rlock2 (const_cast<Playlist
*> (other
.get()));
234 framepos_t end
= start
+ cnt
- 1;
240 for (RegionList::const_iterator i
= other
->regions
.begin(); i
!= other
->regions
.end(); ++i
) {
242 boost::shared_ptr
<Region
> region
;
243 boost::shared_ptr
<Region
> new_region
;
244 frameoffset_t offset
= 0;
245 framepos_t position
= 0;
252 overlap
= region
->coverage (start
, end
);
258 case OverlapInternal
:
259 offset
= start
- region
->position();
266 position
= region
->position() - start
;
267 len
= end
- region
->position();
271 offset
= start
- region
->position();
273 len
= region
->length() - offset
;
276 case OverlapExternal
:
278 position
= region
->position() - start
;
279 len
= region
->length();
283 RegionFactory::region_name (new_name
, region
->name(), false);
287 plist
.add (Properties::start
, region
->start() + offset
);
288 plist
.add (Properties::length
, len
);
289 plist
.add (Properties::name
, new_name
);
290 plist
.add (Properties::layer
, region
->layer());
292 new_region
= RegionFactory::RegionFactory::create (region
, plist
);
294 add_region_internal (new_region
, position
);
298 first_set_state
= false;
300 /* this constructor does NOT notify others (session) */
307 InUse (true); /* EMIT SIGNAL */
318 InUse (false); /* EMIT SIGNAL */
323 Playlist::copy_regions (RegionList
& newlist
) const
325 RegionLock
rlock (const_cast<Playlist
*> (this));
327 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
328 newlist
.push_back (RegionFactory::RegionFactory::create (*i
, true));
333 Playlist::init (bool hide
)
335 add_property (regions
);
336 _xml_node_name
= X_("Playlist");
338 g_atomic_int_set (&block_notifications
, 0);
339 g_atomic_int_set (&ignore_state_changes
, 0);
340 pending_contents_change
= false;
341 pending_length
= false;
342 pending_layering
= false;
343 first_set_state
= true;
351 _edit_mode
= Config
->get_edit_mode();
353 in_partition
= false;
355 _read_data_count
= 0;
357 layer_op_counter
= 0;
359 _explicit_relayering
= false;
361 _session
.history().BeginUndoRedo
.connect_same_thread (*this, boost::bind (&Playlist::begin_undo
, this));
362 _session
.history().EndUndoRedo
.connect_same_thread (*this, boost::bind (&Playlist::end_undo
, this));
364 ContentsChanged
.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty
, this));
367 Playlist::~Playlist ()
369 DEBUG_TRACE (DEBUG::Destruction
, string_compose ("Playlist %1 destructor\n", _name
));
372 RegionLock
rl (this);
374 for (set
<boost::shared_ptr
<Region
> >::iterator i
= all_regions
.begin(); i
!= all_regions
.end(); ++i
) {
375 (*i
)->set_playlist (boost::shared_ptr
<Playlist
>());
379 /* GoingAway must be emitted by derived classes */
383 Playlist::_set_sort_id ()
386 Playlists are given names like <track name>.<id>
387 or <track name>.<edit group name>.<id> where id
388 is an integer. We extract the id and sort by that.
391 size_t dot_position
= _name
.val().find_last_of(".");
393 if (dot_position
== string::npos
) {
396 string t
= _name
.val().substr(dot_position
+ 1);
399 _sort_id
= boost::lexical_cast
<int>(t
);
402 catch (boost::bad_lexical_cast e
) {
409 Playlist::set_name (const string
& str
)
411 /* in a typical situation, a playlist is being used
412 by one diskstream and also is referenced by the
413 Session. if there are more references than that,
414 then don't change the name.
421 bool ret
= SessionObject::set_name(str
);
428 /***********************************************************************
429 CHANGE NOTIFICATION HANDLING
431 Notifications must be delayed till the region_lock is released. This
432 is necessary because handlers for the signals may need to acquire
433 the lock (e.g. to read from the playlist).
434 ***********************************************************************/
437 Playlist::begin_undo ()
444 Playlist::end_undo ()
453 delay_notifications ();
454 g_atomic_int_inc (&ignore_state_changes
);
457 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
459 Playlist::thaw (bool from_undo
)
461 g_atomic_int_dec_and_test (&ignore_state_changes
);
462 release_notifications (from_undo
);
467 Playlist::delay_notifications ()
469 g_atomic_int_inc (&block_notifications
);
470 freeze_length
= _get_extent().second
;
473 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
475 Playlist::release_notifications (bool from_undo
)
477 if (g_atomic_int_dec_and_test (&block_notifications
)) {
478 flush_notifications (from_undo
);
483 Playlist::notify_contents_changed ()
485 if (holding_state ()) {
486 pending_contents_change
= true;
488 pending_contents_change
= false;
489 ContentsChanged(); /* EMIT SIGNAL */
494 Playlist::notify_layering_changed ()
496 if (holding_state ()) {
497 pending_layering
= true;
499 pending_layering
= false;
500 LayeringChanged(); /* EMIT SIGNAL */
505 Playlist::notify_region_removed (boost::shared_ptr
<Region
> r
)
507 if (holding_state ()) {
508 pending_removes
.insert (r
);
509 pending_contents_change
= true;
510 pending_length
= true;
512 /* this might not be true, but we have to act
513 as though it could be.
515 pending_length
= false;
516 LengthChanged (); /* EMIT SIGNAL */
517 pending_contents_change
= false;
518 RegionRemoved (boost::weak_ptr
<Region
> (r
)); /* EMIT SIGNAL */
519 ContentsChanged (); /* EMIT SIGNAL */
524 Playlist::notify_region_moved (boost::shared_ptr
<Region
> r
)
526 Evoral::RangeMove
<framepos_t
> const move (r
->last_position (), r
->length (), r
->position ());
528 if (holding_state ()) {
530 pending_range_moves
.push_back (move
);
534 list
< Evoral::RangeMove
<framepos_t
> > m
;
536 RangesMoved (m
, false);
542 Playlist::notify_region_start_trimmed (boost::shared_ptr
<Region
> r
)
544 if (r
->position() >= r
->last_position()) {
545 /* trimmed shorter */
549 Evoral::Range
<framepos_t
> const extra (r
->position(), r
->last_position());
551 if (holding_state ()) {
553 pending_region_extensions
.push_back (extra
);
557 list
<Evoral::Range
<framepos_t
> > r
;
565 Playlist::notify_region_end_trimmed (boost::shared_ptr
<Region
> r
)
567 if (r
->length() < r
->last_length()) {
568 /* trimmed shorter */
571 Evoral::Range
<framepos_t
> const extra (r
->position() + r
->last_length(), r
->position() + r
->length());
573 if (holding_state ()) {
575 pending_region_extensions
.push_back (extra
);
579 list
<Evoral::Range
<framepos_t
> > r
;
587 Playlist::notify_region_added (boost::shared_ptr
<Region
> r
)
589 /* the length change might not be true, but we have to act
590 as though it could be.
593 if (holding_state()) {
594 pending_adds
.insert (r
);
595 pending_contents_change
= true;
596 pending_length
= true;
599 pending_length
= false;
600 LengthChanged (); /* EMIT SIGNAL */
601 pending_contents_change
= false;
602 RegionAdded (boost::weak_ptr
<Region
> (r
)); /* EMIT SIGNAL */
603 ContentsChanged (); /* EMIT SIGNAL */
608 Playlist::notify_length_changed ()
610 if (holding_state ()) {
611 pending_length
= true;
613 pending_length
= false;
614 LengthChanged(); /* EMIT SIGNAL */
615 pending_contents_change
= false;
616 ContentsChanged (); /* EMIT SIGNAL */
620 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
622 Playlist::flush_notifications (bool from_undo
)
624 set
<boost::shared_ptr
<Region
> > dependent_checks_needed
;
625 set
<boost::shared_ptr
<Region
> >::iterator s
;
626 uint32_t regions_changed
= false;
627 bool check_length
= false;
628 framecnt_t old_length
= 0;
636 if (!pending_bounds
.empty() || !pending_removes
.empty() || !pending_adds
.empty()) {
637 regions_changed
= true;
638 if (!pending_length
) {
639 old_length
= _get_extent ().second
;
644 /* we have no idea what order the regions ended up in pending
645 bounds (it could be based on selection order, for example).
646 so, to preserve layering in the "most recently moved is higher"
647 model, sort them by existing layer, then timestamp them.
650 // RegionSortByLayer cmp;
651 // pending_bounds.sort (cmp);
653 for (RegionList::iterator r
= pending_bounds
.begin(); r
!= pending_bounds
.end(); ++r
) {
654 if (_session
.config
.get_layer_model() == MoveAddHigher
) {
655 timestamp_layer_op (*r
);
657 dependent_checks_needed
.insert (*r
);
660 for (s
= pending_removes
.begin(); s
!= pending_removes
.end(); ++s
) {
661 remove_dependents (*s
);
662 // cerr << _name << " sends RegionRemoved\n";
663 RegionRemoved (boost::weak_ptr
<Region
> (*s
)); /* EMIT SIGNAL */
666 for (s
= pending_adds
.begin(); s
!= pending_adds
.end(); ++s
) {
667 // cerr << _name << " sends RegionAdded\n";
668 /* don't emit RegionAdded signal until relayering is done,
669 so that the region is fully setup by the time
670 anyone hear's that its been added
672 dependent_checks_needed
.insert (*s
);
676 if (old_length
!= _get_extent().second
) {
677 pending_length
= true;
678 // cerr << _name << " length has changed\n";
682 if (pending_length
|| (freeze_length
!= _get_extent().second
)) {
683 pending_length
= false;
684 // cerr << _name << " sends LengthChanged\n";
685 LengthChanged(); /* EMIT SIGNAL */
688 if (regions_changed
|| pending_contents_change
) {
692 pending_contents_change
= false;
693 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
694 ContentsChanged (); /* EMIT SIGNAL */
695 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
698 for (s
= pending_adds
.begin(); s
!= pending_adds
.end(); ++s
) {
699 (*s
)->clear_changes ();
700 RegionAdded (boost::weak_ptr
<Region
> (*s
)); /* EMIT SIGNAL */
703 for (s
= dependent_checks_needed
.begin(); s
!= dependent_checks_needed
.end(); ++s
) {
704 check_dependents (*s
, false);
707 if (!pending_range_moves
.empty ()) {
708 RangesMoved (pending_range_moves
, from_undo
);
711 if (!pending_region_extensions
.empty ()) {
712 RegionsExtended (pending_region_extensions
);
721 Playlist::clear_pending ()
723 pending_adds
.clear ();
724 pending_removes
.clear ();
725 pending_bounds
.clear ();
726 pending_range_moves
.clear ();
727 pending_region_extensions
.clear ();
728 pending_contents_change
= false;
729 pending_length
= false;
732 /*************************************************************
734 *************************************************************/
737 Playlist::add_region (boost::shared_ptr
<Region
> region
, framepos_t position
, float times
, bool auto_partition
)
739 RegionLock
rlock (this);
740 times
= fabs (times
);
742 int itimes
= (int) floor (times
);
744 framepos_t pos
= position
;
746 if (times
== 1 && auto_partition
){
747 partition(pos
- 1, (pos
+ region
->length()), true);
751 add_region_internal (region
, pos
);
752 pos
+= region
->length();
757 /* note that itimes can be zero if we being asked to just
758 insert a single fraction of the region.
761 for (int i
= 0; i
< itimes
; ++i
) {
762 boost::shared_ptr
<Region
> copy
= RegionFactory::create (region
, true);
763 add_region_internal (copy
, pos
);
764 pos
+= region
->length();
767 framecnt_t length
= 0;
769 if (floor (times
) != times
) {
770 length
= (framecnt_t
) floor (region
->length() * (times
- floor (times
)));
772 RegionFactory::region_name (name
, region
->name(), false);
777 plist
.add (Properties::start
, region
->start());
778 plist
.add (Properties::length
, length
);
779 plist
.add (Properties::name
, name
);
780 plist
.add (Properties::layer
, region
->layer());
782 boost::shared_ptr
<Region
> sub
= RegionFactory::create (region
, plist
);
783 add_region_internal (sub
, pos
);
787 possibly_splice_unlocked (position
, (pos
+ length
) - position
, boost::shared_ptr
<Region
>());
791 Playlist::set_region_ownership ()
793 RegionLock
rl (this);
794 RegionList::iterator i
;
795 boost::weak_ptr
<Playlist
> pl (shared_from_this());
797 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
798 (*i
)->set_playlist (pl
);
803 Playlist::add_region_internal (boost::shared_ptr
<Region
> region
, framepos_t position
)
805 if (region
->data_type() != _type
){
809 RegionSortByPosition cmp
;
811 framecnt_t old_length
= 0;
813 if (!holding_state()) {
814 old_length
= _get_extent().second
;
817 if (!first_set_state
) {
818 boost::shared_ptr
<Playlist
> foo (shared_from_this());
819 region
->set_playlist (boost::weak_ptr
<Playlist
>(foo
));
822 region
->set_position (position
, this);
824 timestamp_layer_op (region
);
826 regions
.insert (upper_bound (regions
.begin(), regions
.end(), region
, cmp
), region
);
827 all_regions
.insert (region
);
829 possibly_splice_unlocked (position
, region
->length(), region
);
831 if (!holding_state ()) {
832 /* layers get assigned from XML state, and are not reset during undo/redo */
836 /* we need to notify the existence of new region before checking dependents. Ick. */
838 notify_region_added (region
);
840 if (!holding_state ()) {
842 check_dependents (region
, false);
844 if (old_length
!= _get_extent().second
) {
845 notify_length_changed ();
849 region
->PropertyChanged
.connect_same_thread (region_state_changed_connections
, boost::bind (&Playlist::region_changed_proxy
, this, _1
, boost::weak_ptr
<Region
> (region
)));
855 Playlist::replace_region (boost::shared_ptr
<Region
> old
, boost::shared_ptr
<Region
> newr
, framepos_t pos
)
857 RegionLock
rlock (this);
859 bool old_sp
= _splicing
;
862 remove_region_internal (old
);
863 add_region_internal (newr
, pos
);
867 possibly_splice_unlocked (pos
, old
->length() - newr
->length());
871 Playlist::remove_region (boost::shared_ptr
<Region
> region
)
873 RegionLock
rlock (this);
874 remove_region_internal (region
);
878 Playlist::remove_region_internal (boost::shared_ptr
<Region
> region
)
880 RegionList::iterator i
;
881 framecnt_t old_length
= 0;
884 if (!holding_state()) {
885 old_length
= _get_extent().second
;
890 region
->set_playlist (boost::weak_ptr
<Playlist
>());
893 /* XXX should probably freeze here .... */
895 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
898 framepos_t pos
= (*i
)->position();
899 framecnt_t distance
= (*i
)->length();
903 possibly_splice_unlocked (pos
, -distance
);
905 if (!holding_state ()) {
907 remove_dependents (region
);
909 if (old_length
!= _get_extent().second
) {
910 notify_length_changed ();
914 notify_region_removed (region
);
924 Playlist::get_equivalent_regions (boost::shared_ptr
<Region
> other
, vector
<boost::shared_ptr
<Region
> >& results
)
926 if (Config
->get_use_overlap_equivalency()) {
927 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
928 if ((*i
)->overlap_equivalent (other
)) {
929 results
.push_back ((*i
));
933 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
934 if ((*i
)->equivalent (other
)) {
935 results
.push_back ((*i
));
942 Playlist::get_region_list_equivalent_regions (boost::shared_ptr
<Region
> other
, vector
<boost::shared_ptr
<Region
> >& results
)
944 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
946 if ((*i
) && (*i
)->region_list_equivalent (other
)) {
947 results
.push_back (*i
);
953 Playlist::partition (framepos_t start
, framepos_t end
, bool cut
)
957 partition_internal (start
, end
, cut
, thawlist
);
959 for (RegionList::iterator i
= thawlist
.begin(); i
!= thawlist
.end(); ++i
) {
960 (*i
)->resume_property_changes ();
965 Playlist::partition_internal (framepos_t start
, framepos_t end
, bool cutting
, RegionList
& thawlist
)
967 RegionList new_regions
;
970 RegionLock
rlock (this);
972 boost::shared_ptr
<Region
> region
;
973 boost::shared_ptr
<Region
> current
;
975 RegionList::iterator tmp
;
977 framepos_t pos1
, pos2
, pos3
, pos4
;
981 /* need to work from a copy, because otherwise the regions we add during the process
982 get operated on as well.
985 RegionList copy
= regions
.rlist();
987 for (RegionList::iterator i
= copy
.begin(); i
!= copy
.end(); i
= tmp
) {
994 if (current
->first_frame() >= start
&& current
->last_frame() < end
) {
997 remove_region_internal (current
);
1003 /* coverage will return OverlapStart if the start coincides
1004 with the end point. we do not partition such a region,
1005 so catch this special case.
1008 if (current
->first_frame() >= end
) {
1012 if ((overlap
= current
->coverage (start
, end
)) == OverlapNone
) {
1016 pos1
= current
->position();
1019 pos4
= current
->last_frame();
1021 if (overlap
== OverlapInternal
) {
1022 /* split: we need 3 new regions, the front, middle and end.
1023 cut: we need 2 regions, the front and end.
1028 ---------------*************************------------
1031 ---------------*****++++++++++++++++====------------
1033 ---------------*****----------------====------------
1038 /* "middle" ++++++ */
1040 RegionFactory::region_name (new_name
, current
->name(), false);
1044 plist
.add (Properties::start
, current
->start() + (pos2
- pos1
));
1045 plist
.add (Properties::length
, pos3
- pos2
);
1046 plist
.add (Properties::name
, new_name
);
1047 plist
.add (Properties::layer
, regions
.size());
1048 plist
.add (Properties::automatic
, true);
1049 plist
.add (Properties::left_of_split
, true);
1050 plist
.add (Properties::right_of_split
, true);
1052 region
= RegionFactory::create (current
, plist
);
1053 add_region_internal (region
, start
);
1054 new_regions
.push_back (region
);
1059 RegionFactory::region_name (new_name
, current
->name(), false);
1063 plist
.add (Properties::start
, current
->start() + (pos3
- pos1
));
1064 plist
.add (Properties::length
, pos4
- pos3
);
1065 plist
.add (Properties::name
, new_name
);
1066 plist
.add (Properties::layer
, regions
.size());
1067 plist
.add (Properties::automatic
, true);
1068 plist
.add (Properties::right_of_split
, true);
1070 region
= RegionFactory::create (current
, plist
);
1072 add_region_internal (region
, end
);
1073 new_regions
.push_back (region
);
1077 current
->suspend_property_changes ();
1078 thawlist
.push_back (current
);
1079 current
->cut_end (pos2
- 1, this);
1081 } else if (overlap
== OverlapEnd
) {
1085 ---------------*************************------------
1088 ---------------**************+++++++++++------------
1090 ---------------**************-----------------------
1097 RegionFactory::region_name (new_name
, current
->name(), false);
1101 plist
.add (Properties::start
, current
->start() + (pos2
- pos1
));
1102 plist
.add (Properties::length
, pos4
- pos2
);
1103 plist
.add (Properties::name
, new_name
);
1104 plist
.add (Properties::layer
, regions
.size());
1105 plist
.add (Properties::automatic
, true);
1106 plist
.add (Properties::left_of_split
, true);
1108 region
= RegionFactory::create (current
, plist
);
1110 add_region_internal (region
, start
);
1111 new_regions
.push_back (region
);
1116 current
->suspend_property_changes ();
1117 thawlist
.push_back (current
);
1118 current
->cut_end (pos2
- 1, this);
1120 } else if (overlap
== OverlapStart
) {
1122 /* split: we need 2 regions: the front and the end.
1123 cut: just trim current to skip the cut area
1128 ---------------*************************------------
1132 ---------------****+++++++++++++++++++++------------
1134 -------------------*********************------------
1140 RegionFactory::region_name (new_name
, current
->name(), false);
1144 plist
.add (Properties::start
, current
->start());
1145 plist
.add (Properties::length
, pos3
- pos1
);
1146 plist
.add (Properties::name
, new_name
);
1147 plist
.add (Properties::layer
, regions
.size());
1148 plist
.add (Properties::automatic
, true);
1149 plist
.add (Properties::right_of_split
, true);
1151 region
= RegionFactory::create (current
, plist
);
1153 add_region_internal (region
, pos1
);
1154 new_regions
.push_back (region
);
1159 current
->suspend_property_changes ();
1160 thawlist
.push_back (current
);
1161 current
->trim_front (pos3
, this);
1162 } else if (overlap
== OverlapExternal
) {
1164 /* split: no split required.
1165 cut: remove the region.
1170 ---------------*************************------------
1174 ---------------*************************------------
1176 ----------------------------------------------------
1181 remove_region_internal (current
);
1184 new_regions
.push_back (current
);
1188 in_partition
= false;
1191 for (RegionList::iterator i
= new_regions
.begin(); i
!= new_regions
.end(); ++i
) {
1192 check_dependents (*i
, false);
1196 boost::shared_ptr
<Playlist
>
1197 Playlist::cut_copy (boost::shared_ptr
<Playlist
> (Playlist::*pmf
)(framepos_t
, framecnt_t
,bool), list
<AudioRange
>& ranges
, bool result_is_hidden
)
1199 boost::shared_ptr
<Playlist
> ret
;
1200 boost::shared_ptr
<Playlist
> pl
;
1203 if (ranges
.empty()) {
1204 return boost::shared_ptr
<Playlist
>();
1207 start
= ranges
.front().start
;
1209 for (list
<AudioRange
>::iterator i
= ranges
.begin(); i
!= ranges
.end(); ++i
) {
1211 pl
= (this->*pmf
)((*i
).start
, (*i
).length(), result_is_hidden
);
1213 if (i
== ranges
.begin()) {
1217 /* paste the next section into the nascent playlist,
1218 offset to reflect the start of the first range we
1222 ret
->paste (pl
, (*i
).start
- start
, 1.0f
);
1229 boost::shared_ptr
<Playlist
>
1230 Playlist::cut (list
<AudioRange
>& ranges
, bool result_is_hidden
)
1232 boost::shared_ptr
<Playlist
> (Playlist::*pmf
)(framepos_t
,framecnt_t
,bool) = &Playlist::cut
;
1233 return cut_copy (pmf
, ranges
, result_is_hidden
);
1236 boost::shared_ptr
<Playlist
>
1237 Playlist::copy (list
<AudioRange
>& ranges
, bool result_is_hidden
)
1239 boost::shared_ptr
<Playlist
> (Playlist::*pmf
)(framepos_t
,framecnt_t
,bool) = &Playlist::copy
;
1240 return cut_copy (pmf
, ranges
, result_is_hidden
);
1243 boost::shared_ptr
<Playlist
>
1244 Playlist::cut (framepos_t start
, framecnt_t cnt
, bool result_is_hidden
)
1246 boost::shared_ptr
<Playlist
> the_copy
;
1247 RegionList thawlist
;
1250 snprintf (buf
, sizeof (buf
), "%" PRIu32
, ++subcnt
);
1251 string new_name
= _name
;
1255 if ((the_copy
= PlaylistFactory::create (shared_from_this(), start
, cnt
, new_name
, result_is_hidden
)) == 0) {
1256 return boost::shared_ptr
<Playlist
>();
1259 partition_internal (start
, start
+cnt
-1, true, thawlist
);
1261 for (RegionList::iterator i
= thawlist
.begin(); i
!= thawlist
.end(); ++i
) {
1262 (*i
)->resume_property_changes();
1268 boost::shared_ptr
<Playlist
>
1269 Playlist::copy (framepos_t start
, framecnt_t cnt
, bool result_is_hidden
)
1273 snprintf (buf
, sizeof (buf
), "%" PRIu32
, ++subcnt
);
1274 string new_name
= _name
;
1278 cnt
= min (_get_extent().second
- start
, cnt
);
1279 return PlaylistFactory::create (shared_from_this(), start
, cnt
, new_name
, result_is_hidden
);
1283 Playlist::paste (boost::shared_ptr
<Playlist
> other
, framepos_t position
, float times
)
1285 times
= fabs (times
);
1288 RegionLock
rl1 (this);
1289 RegionLock
rl2 (other
.get());
1291 framecnt_t
const old_length
= _get_extent().second
;
1293 int itimes
= (int) floor (times
);
1294 framepos_t pos
= position
;
1295 framecnt_t
const shift
= other
->_get_extent().second
;
1296 layer_t top_layer
= regions
.size();
1299 for (RegionList::iterator i
= other
->regions
.begin(); i
!= other
->regions
.end(); ++i
) {
1300 boost::shared_ptr
<Region
> copy_of_region
= RegionFactory::create (*i
, true);
1302 /* put these new regions on top of all existing ones, but preserve
1303 the ordering they had in the original playlist.
1306 copy_of_region
->set_layer (copy_of_region
->layer() + top_layer
);
1307 add_region_internal (copy_of_region
, (*i
)->position() + pos
);
1313 /* XXX shall we handle fractional cases at some point? */
1315 if (old_length
!= _get_extent().second
) {
1316 notify_length_changed ();
1327 Playlist::duplicate (boost::shared_ptr
<Region
> region
, framepos_t position
, float times
)
1329 times
= fabs (times
);
1331 RegionLock
rl (this);
1332 int itimes
= (int) floor (times
);
1333 framepos_t pos
= position
+ 1;
1336 boost::shared_ptr
<Region
> copy
= RegionFactory::create (region
, true);
1337 add_region_internal (copy
, pos
);
1338 pos
+= region
->length();
1341 if (floor (times
) != times
) {
1342 framecnt_t length
= (framecnt_t
) floor (region
->length() * (times
- floor (times
)));
1344 RegionFactory::region_name (name
, region
->name(), false);
1349 plist
.add (Properties::start
, region
->start());
1350 plist
.add (Properties::length
, length
);
1351 plist
.add (Properties::name
, name
);
1353 boost::shared_ptr
<Region
> sub
= RegionFactory::create (region
, plist
);
1354 add_region_internal (sub
, pos
);
1360 Playlist::shift (framepos_t at
, frameoffset_t distance
, bool move_intersected
, bool ignore_music_glue
)
1362 RegionLock
rlock (this);
1363 RegionList
copy (regions
.rlist());
1366 for (RegionList::iterator r
= copy
.begin(); r
!= copy
.end(); ++r
) {
1368 if ((*r
)->last_frame() < at
) {
1373 if (at
> (*r
)->first_frame() && at
< (*r
)->last_frame()) {
1374 /* intersected region */
1375 if (!move_intersected
) {
1380 /* do not move regions glued to music time - that
1381 has to be done separately.
1384 if (!ignore_music_glue
&& (*r
)->position_lock_style() != AudioTime
) {
1385 fixup
.push_back (*r
);
1389 (*r
)->set_position ((*r
)->position() + distance
, this);
1392 for (RegionList::iterator r
= fixup
.begin(); r
!= fixup
.end(); ++r
) {
1393 (*r
)->recompute_position_from_lock_style ();
1398 Playlist::split (framepos_t at
)
1400 RegionLock
rlock (this);
1401 RegionList
copy (regions
.rlist());
1403 /* use a copy since this operation can modify the region list
1406 for (RegionList::iterator r
= copy
.begin(); r
!= copy
.end(); ++r
) {
1407 _split_region (*r
, at
);
1412 Playlist::split_region (boost::shared_ptr
<Region
> region
, framepos_t playlist_position
)
1414 RegionLock
rl (this);
1415 _split_region (region
, playlist_position
);
1419 Playlist::_split_region (boost::shared_ptr
<Region
> region
, framepos_t playlist_position
)
1421 if (!region
->covers (playlist_position
)) {
1425 if (region
->position() == playlist_position
||
1426 region
->last_frame() == playlist_position
) {
1430 boost::shared_ptr
<Region
> left
;
1431 boost::shared_ptr
<Region
> right
;
1432 frameoffset_t before
;
1433 frameoffset_t after
;
1437 /* split doesn't change anything about length, so don't try to splice */
1439 bool old_sp
= _splicing
;
1442 before
= playlist_position
- region
->position();
1443 after
= region
->length() - before
;
1445 RegionFactory::region_name (before_name
, region
->name(), false);
1450 plist
.add (Properties::position
, region
->position ());
1451 plist
.add (Properties::length
, before
);
1452 plist
.add (Properties::name
, before_name
);
1453 plist
.add (Properties::left_of_split
, true);
1455 /* note: we must use the version of ::create with an offset here,
1456 since it supplies that offset to the Region constructor, which
1457 is necessary to get audio region gain envelopes right.
1459 left
= RegionFactory::create (region
, 0, plist
);
1462 RegionFactory::region_name (after_name
, region
->name(), false);
1467 plist
.add (Properties::position
, region
->position() + before
);
1468 plist
.add (Properties::length
, after
);
1469 plist
.add (Properties::name
, after_name
);
1470 plist
.add (Properties::right_of_split
, true);
1472 /* same note as above */
1473 right
= RegionFactory::create (region
, before
, plist
);
1476 add_region_internal (left
, region
->position());
1477 add_region_internal (right
, region
->position() + before
);
1479 uint64_t orig_layer_op
= region
->last_layer_op();
1480 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1481 if ((*i
)->last_layer_op() > orig_layer_op
) {
1482 (*i
)->set_last_layer_op( (*i
)->last_layer_op() + 1 );
1486 left
->set_last_layer_op ( orig_layer_op
);
1487 right
->set_last_layer_op ( orig_layer_op
+ 1);
1491 finalize_split_region (region
, left
, right
);
1493 remove_region_internal (region
);
1499 Playlist::possibly_splice (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1501 if (_splicing
|| in_set_state
) {
1502 /* don't respond to splicing moves or state setting */
1506 if (_edit_mode
== Splice
) {
1507 splice_locked (at
, distance
, exclude
);
1512 Playlist::possibly_splice_unlocked (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1514 if (_splicing
|| in_set_state
) {
1515 /* don't respond to splicing moves or state setting */
1519 if (_edit_mode
== Splice
) {
1520 splice_unlocked (at
, distance
, exclude
);
1525 Playlist::splice_locked (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1528 RegionLock
rl (this);
1529 core_splice (at
, distance
, exclude
);
1534 Playlist::splice_unlocked (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1536 core_splice (at
, distance
, exclude
);
1540 Playlist::core_splice (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1544 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1546 if (exclude
&& (*i
) == exclude
) {
1550 if ((*i
)->position() >= at
) {
1551 framepos_t new_pos
= (*i
)->position() + distance
;
1554 } else if (new_pos
>= max_framepos
- (*i
)->length()) {
1555 new_pos
= max_framepos
- (*i
)->length();
1558 (*i
)->set_position (new_pos
, this);
1564 notify_length_changed ();
1568 Playlist::region_bounds_changed (const PropertyChange
& what_changed
, boost::shared_ptr
<Region
> region
)
1570 if (in_set_state
|| _splicing
|| _nudging
|| _shuffling
) {
1574 if (what_changed
.contains (Properties::position
)) {
1576 /* remove it from the list then add it back in
1577 the right place again.
1580 RegionSortByPosition cmp
;
1582 RegionList::iterator i
= find (regions
.begin(), regions
.end(), region
);
1584 if (i
== regions
.end()) {
1585 /* the region bounds are being modified but its not currently
1586 in the region list. we will use its bounds correctly when/if
1593 regions
.insert (upper_bound (regions
.begin(), regions
.end(), region
, cmp
), region
);
1596 if (what_changed
.contains (Properties::position
) || what_changed
.contains (Properties::length
)) {
1598 frameoffset_t delta
= 0;
1600 if (what_changed
.contains (Properties::position
)) {
1601 delta
= region
->position() - region
->last_position();
1604 if (what_changed
.contains (Properties::length
)) {
1605 delta
+= region
->length() - region
->last_length();
1609 possibly_splice (region
->last_position() + region
->last_length(), delta
, region
);
1612 if (holding_state ()) {
1613 pending_bounds
.push_back (region
);
1615 if (_session
.config
.get_layer_model() == MoveAddHigher
) {
1616 /* it moved or changed length, so change the timestamp */
1617 timestamp_layer_op (region
);
1620 notify_length_changed ();
1622 check_dependents (region
, false);
1628 Playlist::region_changed_proxy (const PropertyChange
& what_changed
, boost::weak_ptr
<Region
> weak_region
)
1630 boost::shared_ptr
<Region
> region (weak_region
.lock());
1636 /* this makes a virtual call to the right kind of playlist ... */
1638 region_changed (what_changed
, region
);
1642 Playlist::region_changed (const PropertyChange
& what_changed
, boost::shared_ptr
<Region
> region
)
1644 PropertyChange our_interests
;
1645 PropertyChange bounds
;
1646 PropertyChange pos_and_length
;
1649 if (in_set_state
|| in_flush
) {
1653 our_interests
.add (Properties::muted
);
1654 our_interests
.add (Properties::layer
);
1655 our_interests
.add (Properties::opaque
);
1657 bounds
.add (Properties::start
);
1658 bounds
.add (Properties::position
);
1659 bounds
.add (Properties::length
);
1661 pos_and_length
.add (Properties::position
);
1662 pos_and_length
.add (Properties::length
);
1664 if (what_changed
.contains (bounds
)) {
1665 region_bounds_changed (what_changed
, region
);
1666 save
= !(_splicing
|| _nudging
);
1669 if (what_changed
.contains (our_interests
) && !what_changed
.contains (pos_and_length
)) {
1670 check_dependents (region
, false);
1673 if (what_changed
.contains (Properties::position
) && !what_changed
.contains (Properties::length
)) {
1674 notify_region_moved (region
);
1675 } else if (!what_changed
.contains (Properties::position
) && what_changed
.contains (Properties::length
)) {
1676 notify_region_end_trimmed (region
);
1677 } else if (what_changed
.contains (Properties::position
) && what_changed
.contains (Properties::length
)) {
1678 notify_region_start_trimmed (region
);
1681 /* don't notify about layer changes, since we are the only object that can initiate
1682 them, and we notify in ::relayer()
1685 if (what_changed
.contains (our_interests
)) {
1693 Playlist::drop_regions ()
1695 RegionLock
rl (this);
1697 all_regions
.clear ();
1701 Playlist::sync_all_regions_with_regions ()
1703 RegionLock
rl (this);
1705 all_regions
.clear ();
1707 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1708 all_regions
.insert (*i
);
1713 Playlist::clear (bool with_signals
)
1716 RegionLock
rl (this);
1718 region_state_changed_connections
.drop_connections ();
1720 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1721 pending_removes
.insert (*i
);
1726 for (set
<boost::shared_ptr
<Region
> >::iterator s
= pending_removes
.begin(); s
!= pending_removes
.end(); ++s
) {
1727 remove_dependents (*s
);
1733 for (set
<boost::shared_ptr
<Region
> >::iterator s
= pending_removes
.begin(); s
!= pending_removes
.end(); ++s
) {
1734 RegionRemoved (boost::weak_ptr
<Region
> (*s
)); /* EMIT SIGNAL */
1737 pending_removes
.clear ();
1738 pending_length
= false;
1740 pending_contents_change
= false;
1746 /***********************************************************************
1748 **********************************************************************/
1750 Playlist::RegionList
*
1751 Playlist::regions_at (framepos_t frame
)
1754 RegionLock
rlock (this);
1755 return find_regions_at (frame
);
1759 Playlist::count_regions_at (framepos_t frame
) const
1761 RegionLock
rlock (const_cast<Playlist
*>(this));
1764 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1765 if ((*i
)->covers (frame
)) {
1773 boost::shared_ptr
<Region
>
1774 Playlist::top_region_at (framepos_t frame
)
1777 RegionLock
rlock (this);
1778 RegionList
*rlist
= find_regions_at (frame
);
1779 boost::shared_ptr
<Region
> region
;
1781 if (rlist
->size()) {
1782 RegionSortByLayer cmp
;
1784 region
= rlist
->back();
1791 boost::shared_ptr
<Region
>
1792 Playlist::top_unmuted_region_at (framepos_t frame
)
1795 RegionLock
rlock (this);
1796 RegionList
*rlist
= find_regions_at (frame
);
1798 for (RegionList::iterator i
= rlist
->begin(); i
!= rlist
->end(); ) {
1800 RegionList::iterator tmp
= i
;
1803 if ((*i
)->muted()) {
1810 boost::shared_ptr
<Region
> region
;
1812 if (rlist
->size()) {
1813 RegionSortByLayer cmp
;
1815 region
= rlist
->back();
1822 Playlist::RegionList
*
1823 Playlist::regions_to_read (framepos_t start
, framepos_t end
)
1825 /* Caller must hold lock */
1827 RegionList covering
;
1828 set
<framepos_t
> to_check
;
1829 set
<boost::shared_ptr
<Region
> > unique
;
1831 to_check
.insert (start
);
1832 to_check
.insert (end
);
1834 DEBUG_TRACE (DEBUG::AudioPlayback
, ">>>>> REGIONS TO READ\n");
1836 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1838 /* find all/any regions that span start+end */
1840 switch ((*i
)->coverage (start
, end
)) {
1844 case OverlapInternal
:
1845 covering
.push_back (*i
);
1846 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("toread: will cover %1 (OInternal)\n", (*i
)->name()));
1850 to_check
.insert ((*i
)->position());
1851 if ((*i
)->position() != 0) {
1852 to_check
.insert ((*i
)->position()-1);
1854 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("toread: will check %1 for %2\n", (*i
)->position(), (*i
)->name()));
1855 covering
.push_back (*i
);
1859 to_check
.insert ((*i
)->last_frame());
1860 to_check
.insert ((*i
)->last_frame()+1);
1861 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("toread: will cover %1 (OEnd)\n", (*i
)->name()));
1862 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("\ttoread: will check %1 for %2\n", (*i
)->last_frame(), (*i
)->name()));
1863 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("\ttoread: will check %1 for %2\n", (*i
)->last_frame(), (*i
)->name()));
1864 covering
.push_back (*i
);
1867 case OverlapExternal
:
1868 covering
.push_back (*i
);
1869 to_check
.insert ((*i
)->position());
1870 if ((*i
)->position() != 0) {
1871 to_check
.insert ((*i
)->position()-1);
1873 to_check
.insert ((*i
)->last_frame());
1874 to_check
.insert ((*i
)->last_frame()+1);
1875 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("toread: will cover %1 (OExt)\n", (*i
)->name()));
1876 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("\ttoread: will check %1 for %2\n", (*i
)->position(), (*i
)->name()));
1877 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("\ttoread: will check %1 for %2\n", (*i
)->last_frame(), (*i
)->name()));
1881 /* don't go too far */
1883 if ((*i
)->position() > end
) {
1888 RegionList
* rlist
= new RegionList
;
1890 /* find all the regions that cover each position .... */
1892 if (covering
.size() == 1) {
1894 rlist
->push_back (covering
.front());
1895 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("Just one covering region (%1)\n", covering
.front()->name()));
1900 for (set
<framepos_t
>::iterator t
= to_check
.begin(); t
!= to_check
.end(); ++t
) {
1904 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("++++ Considering %1\n", *t
));
1906 for (RegionList::iterator x
= covering
.begin(); x
!= covering
.end(); ++x
) {
1908 if ((*x
)->covers (*t
)) {
1909 here
.push_back (*x
);
1910 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("region %1 covers %2\n",
1914 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("region %1 does NOT covers %2\n",
1921 RegionSortByLayer cmp
;
1924 /* ... and get the top/transparent regions at "here" */
1926 for (RegionList::reverse_iterator c
= here
.rbegin(); c
!= here
.rend(); ++c
) {
1930 if ((*c
)->opaque()) {
1932 /* the other regions at this position are hidden by this one */
1933 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("%1 is opaque, ignore all others\n",
1940 for (set
<boost::shared_ptr
<Region
> >::iterator s
= unique
.begin(); s
!= unique
.end(); ++s
) {
1941 rlist
->push_back (*s
);
1944 if (rlist
->size() > 1) {
1945 /* now sort by time order */
1947 RegionSortByPosition cmp
;
1952 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist
->size()));
1957 Playlist::RegionList
*
1958 Playlist::find_regions_at (framepos_t frame
)
1960 /* Caller must hold lock */
1962 RegionList
*rlist
= new RegionList
;
1964 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1965 if ((*i
)->covers (frame
)) {
1966 rlist
->push_back (*i
);
1973 Playlist::RegionList
*
1974 Playlist::regions_touched (framepos_t start
, framepos_t end
)
1976 RegionLock
rlock (this);
1977 RegionList
*rlist
= new RegionList
;
1979 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1980 if ((*i
)->coverage (start
, end
) != OverlapNone
) {
1981 rlist
->push_back (*i
);
1989 Playlist::find_next_transient (framepos_t from
, int dir
)
1991 RegionLock
rlock (this);
1992 AnalysisFeatureList points
;
1993 AnalysisFeatureList these_points
;
1995 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1997 if ((*i
)->last_frame() < from
) {
2001 if ((*i
)->first_frame() > from
) {
2006 (*i
)->get_transients (these_points
);
2008 /* add first frame, just, err, because */
2010 these_points
.push_back ((*i
)->first_frame());
2012 points
.insert (points
.end(), these_points
.begin(), these_points
.end());
2013 these_points
.clear ();
2016 if (points
.empty()) {
2020 TransientDetector::cleanup_transients (points
, _session
.frame_rate(), 3.0);
2021 bool reached
= false;
2024 for (AnalysisFeatureList::iterator x
= points
.begin(); x
!= points
.end(); ++x
) {
2029 if (reached
&& (*x
) > from
) {
2034 for (AnalysisFeatureList::reverse_iterator x
= points
.rbegin(); x
!= points
.rend(); ++x
) {
2039 if (reached
&& (*x
) < from
) {
2048 boost::shared_ptr
<Region
>
2049 Playlist::find_next_region (framepos_t frame
, RegionPoint point
, int dir
)
2051 RegionLock
rlock (this);
2052 boost::shared_ptr
<Region
> ret
;
2053 framepos_t closest
= max_framepos
;
2055 bool end_iter
= false;
2057 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2061 frameoffset_t distance
;
2062 boost::shared_ptr
<Region
> r
= (*i
);
2067 pos
= r
->first_frame ();
2070 pos
= r
->last_frame ();
2073 pos
= r
->sync_position ();
2078 case 1: /* forwards */
2081 if ((distance
= pos
- frame
) < closest
) {
2090 default: /* backwards */
2093 if ((distance
= frame
- pos
) < closest
) {
2110 Playlist::find_next_region_boundary (framepos_t frame
, int dir
)
2112 RegionLock
rlock (this);
2114 framepos_t closest
= max_framepos
;
2115 framepos_t ret
= -1;
2119 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2121 boost::shared_ptr
<Region
> r
= (*i
);
2122 frameoffset_t distance
;
2124 if (r
->first_frame() > frame
) {
2126 distance
= r
->first_frame() - frame
;
2128 if (distance
< closest
) {
2129 ret
= r
->first_frame();
2134 if (r
->last_frame () > frame
) {
2136 distance
= r
->last_frame () - frame
;
2138 if (distance
< closest
) {
2139 ret
= r
->last_frame ();
2147 for (RegionList::reverse_iterator i
= regions
.rbegin(); i
!= regions
.rend(); ++i
) {
2149 boost::shared_ptr
<Region
> r
= (*i
);
2150 frameoffset_t distance
;
2152 if (r
->last_frame() < frame
) {
2154 distance
= frame
- r
->last_frame();
2156 if (distance
< closest
) {
2157 ret
= r
->last_frame();
2162 if (r
->first_frame() < frame
) {
2164 distance
= frame
- r
->first_frame();
2166 if (distance
< closest
) {
2167 ret
= r
->first_frame();
2178 /***********************************************************************/
2184 Playlist::mark_session_dirty ()
2186 if (!in_set_state
&& !holding_state ()) {
2187 _session
.set_dirty();
2192 Playlist::rdiff (vector
<Command
*>& cmds
) const
2194 RegionLock
rlock (const_cast<Playlist
*> (this));
2195 Stateful::rdiff (cmds
);
2199 Playlist::clear_owned_changes ()
2201 RegionLock
rlock (this);
2202 Stateful::clear_owned_changes ();
2206 Playlist::update (const RegionListProperty::ChangeRecord
& change
)
2208 DEBUG_TRACE (DEBUG::Properties
, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2209 name(), change
.added
.size(), change
.removed
.size()));
2212 /* add the added regions */
2213 for (RegionListProperty::ChangeContainer::iterator i
= change
.added
.begin(); i
!= change
.added
.end(); ++i
) {
2214 add_region ((*i
), (*i
)->position());
2216 /* remove the removed regions */
2217 for (RegionListProperty::ChangeContainer::iterator i
= change
.removed
.begin(); i
!= change
.removed
.end(); ++i
) {
2225 Playlist::set_state (const XMLNode
& node
, int version
)
2229 XMLNodeConstIterator niter
;
2230 XMLPropertyList plist
;
2231 XMLPropertyConstIterator piter
;
2233 boost::shared_ptr
<Region
> region
;
2238 if (node
.name() != "Playlist") {
2245 plist
= node
.properties();
2247 for (piter
= plist
.begin(); piter
!= plist
.end(); ++piter
) {
2251 if (prop
->name() == X_("name")) {
2252 _name
= prop
->value();
2254 } else if (prop
->name() == X_("id")) {
2255 _id
= prop
->value();
2256 } else if (prop
->name() == X_("orig_diskstream_id")) {
2257 _orig_diskstream_id
= prop
->value ();
2258 } else if (prop
->name() == X_("frozen")) {
2259 _frozen
= string_is_affirmative (prop
->value());
2265 nlist
= node
.children();
2267 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
2271 if (child
->name() == "Region") {
2273 if ((prop
= child
->property ("id")) == 0) {
2274 error
<< _("region state node has no ID, ignored") << endmsg
;
2278 ID id
= prop
->value ();
2280 if ((region
= region_by_id (id
))) {
2282 region
->suspend_property_changes ();
2284 if (region
->set_state (*child
, version
)) {
2285 region
->resume_property_changes ();
2289 } else if ((region
= RegionFactory::create (_session
, *child
, true)) != 0) {
2290 region
->suspend_property_changes ();
2292 error
<< _("Playlist: cannot create region from XML") << endmsg
;
2297 add_region (region
, region
->position(), 1.0);
2299 // So that layer_op ordering doesn't get screwed up
2300 region
->set_last_layer_op( region
->layer());
2301 region
->resume_property_changes ();
2305 /* update dependents, which was not done during add_region_internal
2306 due to in_set_state being true
2309 for (RegionList::iterator r
= regions
.begin(); r
!= regions
.end(); ++r
) {
2310 check_dependents (*r
, false);
2314 notify_contents_changed ();
2317 first_set_state
= false;
2322 Playlist::get_state()
2324 return state (true);
2328 Playlist::get_template()
2330 return state (false);
2333 /** @param full_state true to include regions in the returned state, otherwise false.
2336 Playlist::state (bool full_state
)
2338 XMLNode
*node
= new XMLNode (X_("Playlist"));
2341 node
->add_property (X_("id"), id().to_s());
2342 node
->add_property (X_("name"), _name
);
2343 node
->add_property (X_("type"), _type
.to_string());
2345 _orig_diskstream_id
.print (buf
, sizeof (buf
));
2346 node
->add_property (X_("orig_diskstream_id"), buf
);
2347 node
->add_property (X_("frozen"), _frozen
? "yes" : "no");
2350 RegionLock
rlock (this, false);
2352 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2353 node
->add_child_nocopy ((*i
)->get_state());
2358 node
->add_child_copy (*_extra_xml
);
2365 Playlist::empty() const
2367 RegionLock
rlock (const_cast<Playlist
*>(this), false);
2368 return regions
.empty();
2372 Playlist::n_regions() const
2374 RegionLock
rlock (const_cast<Playlist
*>(this), false);
2375 return regions
.size();
2378 pair
<framepos_t
, framepos_t
>
2379 Playlist::get_extent () const
2381 RegionLock
rlock (const_cast<Playlist
*>(this), false);
2382 return _get_extent ();
2385 pair
<framepos_t
, framepos_t
>
2386 Playlist::_get_extent () const
2388 pair
<framepos_t
, framepos_t
> ext (max_framepos
, 0);
2390 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2391 pair
<framepos_t
, framepos_t
> const e ((*i
)->position(), (*i
)->position() + (*i
)->length());
2392 if (e
.first
< ext
.first
) {
2393 ext
.first
= e
.first
;
2395 if (e
.second
> ext
.second
) {
2396 ext
.second
= e
.second
;
2404 Playlist::bump_name (string name
, Session
&session
)
2406 string newname
= name
;
2409 newname
= bump_name_once (newname
, '.');
2410 } while (session
.playlists
->by_name (newname
)!=NULL
);
2417 Playlist::top_layer() const
2419 RegionLock
rlock (const_cast<Playlist
*> (this));
2422 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2423 top
= max (top
, (*i
)->layer());
2429 Playlist::set_edit_mode (EditMode mode
)
2434 /********************
2436 ********************/
2439 Playlist::relayer ()
2441 /* never compute layers when changing state for undo/redo or setting from XML */
2443 if (in_update
|| in_set_state
) {
2447 bool changed
= false;
2449 /* Build up a new list of regions on each layer, stored in a set of lists
2450 each of which represent some period of time on some layer. The idea
2451 is to avoid having to search the entire region list to establish whether
2452 each region overlaps another */
2454 /* how many pieces to divide this playlist's time up into */
2455 int const divisions
= 512;
2457 /* find the start and end positions of the regions on this playlist */
2458 framepos_t start
= INT64_MAX
;
2460 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2461 start
= min (start
, (*i
)->position());
2462 end
= max (end
, (*i
)->position() + (*i
)->length());
2465 /* hence the size of each time division */
2466 double const division_size
= (end
- start
) / double (divisions
);
2468 vector
<vector
<RegionList
> > layers
;
2469 layers
.push_back (vector
<RegionList
> (divisions
));
2471 /* we want to go through regions from desired lowest to desired highest layer,
2472 which depends on the layer model
2475 RegionList copy
= regions
.rlist();
2477 /* sort according to the model and the layering mode that we're in */
2479 if (_explicit_relayering
) {
2481 copy
.sort (RegionSortByLayerWithPending ());
2483 } else if (_session
.config
.get_layer_model() == MoveAddHigher
|| _session
.config
.get_layer_model() == AddHigher
) {
2485 copy
.sort (RegionSortByLastLayerOp ());
2490 for (RegionList::iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
2492 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2493 (*i
)->set_pending_explicit_relayer (false);
2495 /* find the time divisions that this region covers; if there are no regions on the list,
2496 division_size will equal 0 and in this case we'll just say that
2497 start_division = end_division = 0.
2499 int start_division
= 0;
2500 int end_division
= 0;
2502 if (division_size
> 0) {
2503 start_division
= floor ( ((*i
)->position() - start
) / division_size
);
2504 end_division
= floor ( ((*i
)->position() + (*i
)->length() - start
) / division_size
);
2505 if (end_division
== divisions
) {
2510 assert (divisions
== 0 || end_division
< divisions
);
2512 /* find the lowest layer that this region can go on */
2513 size_t j
= layers
.size();
2515 /* try layer j - 1; it can go on if it overlaps no other region
2516 that is already on that layer
2519 bool overlap
= false;
2520 for (int k
= start_division
; k
<= end_division
; ++k
) {
2521 RegionList::iterator l
= layers
[j
-1][k
].begin ();
2522 while (l
!= layers
[j
-1][k
].end()) {
2523 if ((*l
)->overlap_equivalent (*i
)) {
2536 /* overlap, so we must use layer j */
2543 if (j
== layers
.size()) {
2544 /* we need a new layer for this region */
2545 layers
.push_back (vector
<RegionList
> (divisions
));
2548 /* put a reference to this region in each of the divisions that it exists in */
2549 for (int k
= start_division
; k
<= end_division
; ++k
) {
2550 layers
[j
][k
].push_back (*i
);
2553 if ((*i
)->layer() != j
) {
2557 (*i
)->set_layer (j
);
2561 notify_layering_changed ();
2565 /* XXX these layer functions are all deprecated */
2568 Playlist::raise_region (boost::shared_ptr
<Region
> region
)
2570 uint32_t top
= regions
.size() - 1;
2571 layer_t target
= region
->layer() + 1U;
2573 if (target
>= top
) {
2574 /* its already at the effective top */
2578 move_region_to_layer (target
, region
, 1);
2582 Playlist::lower_region (boost::shared_ptr
<Region
> region
)
2584 if (region
->layer() == 0) {
2585 /* its already at the bottom */
2589 layer_t target
= region
->layer() - 1U;
2591 move_region_to_layer (target
, region
, -1);
2595 Playlist::raise_region_to_top (boost::shared_ptr
<Region
> region
)
2597 /* does nothing useful if layering mode is later=higher */
2598 switch (_session
.config
.get_layer_model()) {
2605 layer_t top
= regions
.size() - 1;
2607 if (region
->layer() >= top
) {
2608 /* already on the top */
2612 move_region_to_layer (top
, region
, 1);
2613 /* mark the region's last_layer_op as now, so that it remains on top when
2614 doing future relayers (until something else takes over)
2616 timestamp_layer_op (region
);
2620 Playlist::lower_region_to_bottom (boost::shared_ptr
<Region
> region
)
2622 /* does nothing useful if layering mode is later=higher */
2623 switch (_session
.config
.get_layer_model()) {
2630 if (region
->layer() == 0) {
2631 /* already on the bottom */
2635 move_region_to_layer (0, region
, -1);
2636 /* force region's last layer op to zero so that it stays at the bottom
2637 when doing future relayers
2639 region
->set_last_layer_op (0);
2643 Playlist::move_region_to_layer (layer_t target_layer
, boost::shared_ptr
<Region
> region
, int dir
)
2645 RegionList::iterator i
;
2646 typedef pair
<boost::shared_ptr
<Region
>,layer_t
> LayerInfo
;
2647 list
<LayerInfo
> layerinfo
;
2650 RegionLock
rlock (const_cast<Playlist
*> (this));
2652 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2662 /* region is moving up, move all regions on intermediate layers
2666 if ((*i
)->layer() > region
->layer() && (*i
)->layer() <= target_layer
) {
2667 dest
= (*i
)->layer() - 1;
2674 /* region is moving down, move all regions on intermediate layers
2678 if ((*i
)->layer() < region
->layer() && (*i
)->layer() >= target_layer
) {
2679 dest
= (*i
)->layer() + 1;
2689 newpair
.second
= dest
;
2691 layerinfo
.push_back (newpair
);
2697 /* now reset the layers without holding the region lock */
2699 for (list
<LayerInfo
>::iterator x
= layerinfo
.begin(); x
!= layerinfo
.end(); ++x
) {
2700 x
->first
->set_layer (x
->second
);
2703 region
->set_layer (target_layer
);
2705 /* now check all dependents, since we changed the layering */
2707 for (list
<LayerInfo
>::iterator x
= layerinfo
.begin(); x
!= layerinfo
.end(); ++x
) {
2708 check_dependents (x
->first
, false);
2711 check_dependents (region
, false);
2712 notify_layering_changed ();
2720 Playlist::nudge_after (framepos_t start
, framecnt_t distance
, bool forwards
)
2722 RegionList::iterator i
;
2728 RegionLock
rlock (const_cast<Playlist
*> (this));
2730 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2732 if ((*i
)->position() >= start
) {
2738 if ((*i
)->last_frame() > max_framepos
- distance
) {
2739 new_pos
= max_framepos
- (*i
)->length();
2741 new_pos
= (*i
)->position() + distance
;
2746 if ((*i
)->position() > distance
) {
2747 new_pos
= (*i
)->position() - distance
;
2753 (*i
)->set_position (new_pos
, this);
2761 notify_length_changed ();
2767 Playlist::uses_source (boost::shared_ptr
<const Source
> src
) const
2769 RegionLock
rlock (const_cast<Playlist
*> (this));
2771 for (set
<boost::shared_ptr
<Region
> >::iterator r
= all_regions
.begin(); r
!= all_regions
.end(); ++r
) {
2772 if ((*r
)->uses_source (src
)) {
2780 boost::shared_ptr
<Region
>
2781 Playlist::find_region (const ID
& id
) const
2783 RegionLock
rlock (const_cast<Playlist
*> (this));
2785 /* searches all regions currently in use by the playlist */
2787 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2788 if ((*i
)->id() == id
) {
2793 return boost::shared_ptr
<Region
> ();
2797 Playlist::region_use_count (boost::shared_ptr
<Region
> r
) const
2799 RegionLock
rlock (const_cast<Playlist
*> (this));
2802 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2811 boost::shared_ptr
<Region
>
2812 Playlist::region_by_id (const ID
& id
) const
2814 /* searches all regions ever added to this playlist */
2816 for (set
<boost::shared_ptr
<Region
> >::iterator i
= all_regions
.begin(); i
!= all_regions
.end(); ++i
) {
2817 if ((*i
)->id() == id
) {
2821 return boost::shared_ptr
<Region
> ();
2825 Playlist::dump () const
2827 boost::shared_ptr
<Region
> r
;
2829 cerr
<< "Playlist \"" << _name
<< "\" " << endl
2830 << regions
.size() << " regions "
2833 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2835 cerr
<< " " << r
->name() << " ["
2836 << r
->start() << "+" << r
->length()
2846 Playlist::set_frozen (bool yn
)
2852 Playlist::timestamp_layer_op (boost::shared_ptr
<Region
> region
)
2854 region
->set_last_layer_op (++layer_op_counter
);
2859 Playlist::shuffle (boost::shared_ptr
<Region
> region
, int dir
)
2863 if (region
->locked()) {
2870 RegionLock
rlock (const_cast<Playlist
*> (this));
2875 RegionList::iterator next
;
2877 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2878 if ((*i
) == region
) {
2882 if (next
!= regions
.end()) {
2884 if ((*next
)->locked()) {
2890 if ((*next
)->position() != region
->last_frame() + 1) {
2891 /* they didn't used to touch, so after shuffle,
2892 just have them swap positions.
2894 new_pos
= (*next
)->position();
2896 /* they used to touch, so after shuffle,
2897 make sure they still do. put the earlier
2898 region where the later one will end after
2901 new_pos
= region
->position() + (*next
)->length();
2904 (*next
)->set_position (region
->position(), this);
2905 region
->set_position (new_pos
, this);
2907 /* avoid a full sort */
2909 regions
.erase (i
); // removes the region from the list */
2911 regions
.insert (next
, region
); // adds it back after next
2920 RegionList::iterator prev
= regions
.end();
2922 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); prev
= i
, ++i
) {
2923 if ((*i
) == region
) {
2925 if (prev
!= regions
.end()) {
2927 if ((*prev
)->locked()) {
2932 if (region
->position() != (*prev
)->last_frame() + 1) {
2933 /* they didn't used to touch, so after shuffle,
2934 just have them swap positions.
2936 new_pos
= region
->position();
2938 /* they used to touch, so after shuffle,
2939 make sure they still do. put the earlier
2940 one where the later one will end after
2942 new_pos
= (*prev
)->position() + region
->length();
2945 region
->set_position ((*prev
)->position(), this);
2946 (*prev
)->set_position (new_pos
, this);
2948 /* avoid a full sort */
2950 regions
.erase (i
); // remove region
2951 regions
.insert (prev
, region
); // insert region before prev
2967 check_dependents (region
, false);
2969 notify_contents_changed();
2975 Playlist::region_is_shuffle_constrained (boost::shared_ptr
<Region
>)
2977 RegionLock
rlock (const_cast<Playlist
*> (this));
2979 if (regions
.size() > 1) {
2987 Playlist::update_after_tempo_map_change ()
2989 RegionLock
rlock (const_cast<Playlist
*> (this));
2990 RegionList
copy (regions
.rlist());
2994 for (RegionList::iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
2995 (*i
)->update_position_after_tempo_map_change ();
3002 Playlist::foreach_region (boost::function
<void(boost::shared_ptr
<Region
>)> s
)
3004 RegionLock
rl (this, false);
3005 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
3011 Playlist::set_explicit_relayering (bool e
)
3013 if (e
== false && _explicit_relayering
== true) {
3015 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
3016 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
3017 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
3018 at this point would keep regions on the same layers.
3020 From then on in, it's just you and your towel.
3023 RegionLock
rl (this);
3024 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
3025 (*i
)->set_last_layer_op ((*i
)->layer ());
3029 _explicit_relayering
= e
;
3034 Playlist::has_region_at (framepos_t
const p
) const
3036 RegionLock (const_cast<Playlist
*> (this));
3038 RegionList::const_iterator i
= regions
.begin ();
3039 while (i
!= regions
.end() && !(*i
)->covers (p
)) {
3043 return (i
!= regions
.end());
3046 /** Remove any region that uses a given source */
3048 Playlist::remove_region_by_source (boost::shared_ptr
<Source
> s
)
3050 RegionLock
rl (this);
3052 RegionList::iterator i
= regions
.begin();
3053 while (i
!= regions
.end()) {
3054 RegionList::iterator j
= i
;
3057 if ((*i
)->uses_source (s
)) {
3058 remove_region_internal (*i
);
3065 /** Look from a session frame time and find the start time of the next region
3066 * which is on the top layer of this playlist.
3067 * @param t Time to look from.
3068 * @return Position of next top-layered region, or max_framepos if there isn't one.
3071 Playlist::find_next_top_layer_position (framepos_t t
) const
3073 RegionLock
rlock (const_cast<Playlist
*> (this));
3075 layer_t
const top
= top_layer ();
3077 RegionList copy
= regions
.rlist ();
3078 copy
.sort (RegionSortByPosition ());
3080 for (RegionList::const_iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
3081 if ((*i
)->position() >= t
&& (*i
)->layer() == top
) {
3082 return (*i
)->position();
3086 return max_framepos
;
3090 Playlist::join (const RegionList
& r
, const std::string
& name
)
3093 uint32_t channels
= 0;
3095 framepos_t earliest_position
= max_framepos
;
3097 boost::shared_ptr
<Playlist
> pl
= PlaylistFactory::create (_type
, _session
, name
, true);
3099 for (RegionList::const_iterator i
= r
.begin(); i
!= r
.end(); ++i
) {
3100 earliest_position
= min (earliest_position
, (*i
)->position());
3103 for (RegionList::const_iterator i
= r
.begin(); i
!= r
.end(); ++i
) {
3105 /* copy the region */
3107 boost::shared_ptr
<Region
> original_region
= (*i
);
3108 boost::shared_ptr
<Region
> copied_region
= RegionFactory::create (original_region
, false);
3110 /* make position relative to zero */
3112 pl
->add_region (copied_region
, original_region
->position() - earliest_position
);
3114 /* use the maximum number of channels for any region */
3116 channels
= max (channels
, original_region
->n_channels());
3118 /* it will go above the layer of the highest existing region */
3120 layer
= max (layer
, original_region
->layer());
3123 /* now create a new PlaylistSource for each channel in the new playlist */
3126 pair
<framepos_t
,framepos_t
> extent
= pl
->get_extent();
3128 for (uint32_t chn
= 0; chn
< channels
; ++chn
) {
3129 sources
.push_back (SourceFactory::createFromPlaylist (_type
, _session
, pl
, name
, chn
, 0, extent
.second
, false, false));
3132 /* now a new region using the list of sources */
3134 plist
.add (Properties::start
, 0);
3135 plist
.add (Properties::length
, extent
.second
);
3136 plist
.add (Properties::name
, name
);
3137 plist
.add (Properties::layer
, layer
+1);
3139 boost::shared_ptr
<Region
> compound_region
= RegionFactory::create (sources
, plist
, true);
3141 /* remove all the selected regions from the current playlist
3146 for (RegionList::const_iterator i
= r
.begin(); i
!= r
.end(); ++i
) {
3150 /* add the new region at the right location */
3152 add_region (compound_region
, earliest_position
);
3158 Playlist::max_source_level () const
3160 RegionLock
rlock (const_cast<Playlist
*> (this));
3163 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
3164 lvl
= max (lvl
, (*i
)->max_source_level());
3172 Playlist::count_joined_regions () const
3174 RegionLock
rlock (const_cast<Playlist
*> (this));
3177 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
3178 if ((*i
)->max_source_level() > 0) {