2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #define __STDC_LIMIT_MACROS
31 #include <boost/lexical_cast.hpp>
33 #include "pbd/failed_constructor.h"
34 #include "pbd/stateful_diff_command.h"
35 #include "pbd/xml++.h"
37 #include "ardour/debug.h"
38 #include "ardour/playlist.h"
39 #include "ardour/session.h"
40 #include "ardour/region.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/playlist_factory.h"
43 #include "ardour/transient_detector.h"
44 #include "ardour/session_playlists.h"
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", Properties::regions
.property_id
));
109 RegionListProperty::RegionListProperty (Playlist
& pl
)
110 : SequenceProperty
<std::list
<boost::shared_ptr
<Region
> > > (Properties::regions
.property_id
, boost::bind (&Playlist::update
, &pl
, _1
))
115 boost::shared_ptr
<Region
>
116 RegionListProperty::lookup_id (const ID
& id
)
118 boost::shared_ptr
<Region
> ret
= _playlist
.region_by_id (id
);
121 ret
= RegionFactory::region_by_id (id
);
128 RegionListProperty::copy_for_history () const
130 RegionListProperty
* copy
= new RegionListProperty (_playlist
);
131 /* this is all we need */
132 copy
->_change
= _change
;
137 RegionListProperty::diff (PropertyList
& undo
, PropertyList
& redo
, Command
* cmd
) const
140 /* list of the removed/added regions since clear_history() was last called */
141 RegionListProperty
* a
= copy_for_history ();
143 /* the same list, but with removed/added lists swapped (for undo purposes) */
144 RegionListProperty
* b
= copy_for_history ();
145 b
->invert_changes ();
148 /* whenever one of the regions emits DropReferences, make sure
149 that the Destructible we've been told to notify hears about
150 it. the Destructible is likely to be the Command being built
154 for (set
<boost::shared_ptr
<Region
> >::iterator i
= a
->change().added
.begin(); i
!= a
->change().added
.end(); ++i
) {
155 (*i
)->DropReferences
.connect_same_thread (*cmd
, boost::bind (&Destructible::drop_references
, cmd
));
164 Playlist::Playlist (Session
& sess
, string nom
, DataType type
, bool hide
)
165 : SessionObject(sess
, nom
)
170 first_set_state
= false;
176 Playlist::Playlist (Session
& sess
, const XMLNode
& node
, DataType type
, bool hide
)
177 : SessionObject(sess
, "unnamed playlist")
183 const XMLProperty
* prop
= node
.property("type");
184 assert(!prop
|| DataType(prop
->value()) == _type
);
188 _name
= "unnamed"; /* reset by set_state */
191 /* set state called by derived class */
194 Playlist::Playlist (boost::shared_ptr
<const Playlist
> other
, string namestr
, bool hide
)
195 : SessionObject(other
->_session
, namestr
)
197 , _type(other
->_type
)
198 , _orig_diskstream_id (other
->_orig_diskstream_id
)
203 other
->copy_regions (tmp
);
207 for (list
<boost::shared_ptr
<Region
> >::iterator x
= tmp
.begin(); x
!= tmp
.end(); ++x
) {
208 add_region_internal( (*x
), (*x
)->position());
213 _splicing
= other
->_splicing
;
214 _nudging
= other
->_nudging
;
215 _edit_mode
= other
->_edit_mode
;
218 first_set_state
= false;
220 in_partition
= false;
222 _read_data_count
= 0;
223 _frozen
= other
->_frozen
;
225 layer_op_counter
= other
->layer_op_counter
;
226 freeze_length
= other
->freeze_length
;
229 Playlist::Playlist (boost::shared_ptr
<const Playlist
> other
, framepos_t start
, framecnt_t cnt
, string str
, bool hide
)
230 : SessionObject(other
->_session
, str
)
232 , _type(other
->_type
)
233 , _orig_diskstream_id (other
->_orig_diskstream_id
)
235 RegionLock
rlock2 (const_cast<Playlist
*> (other
.get()));
237 framepos_t end
= start
+ cnt
- 1;
243 for (RegionList::const_iterator i
= other
->regions
.begin(); i
!= other
->regions
.end(); ++i
) {
245 boost::shared_ptr
<Region
> region
;
246 boost::shared_ptr
<Region
> new_region
;
247 frameoffset_t offset
= 0;
248 framepos_t position
= 0;
255 overlap
= region
->coverage (start
, end
);
261 case OverlapInternal
:
262 offset
= start
- region
->position();
269 position
= region
->position() - start
;
270 len
= end
- region
->position();
274 offset
= start
- region
->position();
276 len
= region
->length() - offset
;
279 case OverlapExternal
:
281 position
= region
->position() - start
;
282 len
= region
->length();
286 RegionFactory::region_name (new_name
, region
->name(), false);
290 plist
.add (Properties::start
, region
->start() + offset
);
291 plist
.add (Properties::length
, len
);
292 plist
.add (Properties::name
, new_name
);
293 plist
.add (Properties::layer
, region
->layer());
295 new_region
= RegionFactory::RegionFactory::create (region
, plist
);
297 add_region_internal (new_region
, position
);
301 first_set_state
= false;
303 /* this constructor does NOT notify others (session) */
310 InUse (true); /* EMIT SIGNAL */
321 InUse (false); /* EMIT SIGNAL */
326 Playlist::copy_regions (RegionList
& newlist
) const
328 RegionLock
rlock (const_cast<Playlist
*> (this));
330 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
331 newlist
.push_back (RegionFactory::RegionFactory::create (*i
));
336 Playlist::init (bool hide
)
338 add_property (regions
);
339 _xml_node_name
= X_("Playlist");
341 g_atomic_int_set (&block_notifications
, 0);
342 g_atomic_int_set (&ignore_state_changes
, 0);
343 pending_contents_change
= false;
344 pending_length
= false;
345 pending_layering
= false;
346 first_set_state
= true;
354 _edit_mode
= Config
->get_edit_mode();
356 in_partition
= false;
358 _read_data_count
= 0;
360 layer_op_counter
= 0;
362 _explicit_relayering
= false;
364 _session
.history().BeginUndoRedo
.connect_same_thread (*this, boost::bind (&Playlist::begin_undo
, this));
365 _session
.history().EndUndoRedo
.connect_same_thread (*this, boost::bind (&Playlist::end_undo
, this));
367 ContentsChanged
.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty
, this));
370 Playlist::~Playlist ()
372 DEBUG_TRACE (DEBUG::Destruction
, string_compose ("Playlist %1 destructor\n", _name
));
375 RegionLock
rl (this);
377 for (set
<boost::shared_ptr
<Region
> >::iterator i
= all_regions
.begin(); i
!= all_regions
.end(); ++i
) {
378 (*i
)->set_playlist (boost::shared_ptr
<Playlist
>());
382 /* GoingAway must be emitted by derived classes */
386 Playlist::_set_sort_id ()
389 Playlists are given names like <track name>.<id>
390 or <track name>.<edit group name>.<id> where id
391 is an integer. We extract the id and sort by that.
394 size_t dot_position
= _name
.val().find_last_of(".");
396 if (dot_position
== string::npos
) {
399 string t
= _name
.val().substr(dot_position
+ 1);
402 _sort_id
= boost::lexical_cast
<int>(t
);
405 catch (boost::bad_lexical_cast e
) {
412 Playlist::set_name (const string
& str
)
414 /* in a typical situation, a playlist is being used
415 by one diskstream and also is referenced by the
416 Session. if there are more references than that,
417 then don't change the name.
424 bool ret
= SessionObject::set_name(str
);
431 /***********************************************************************
432 CHANGE NOTIFICATION HANDLING
434 Notifications must be delayed till the region_lock is released. This
435 is necessary because handlers for the signals may need to acquire
436 the lock (e.g. to read from the playlist).
437 ***********************************************************************/
440 Playlist::begin_undo ()
447 Playlist::end_undo ()
456 delay_notifications ();
457 g_atomic_int_inc (&ignore_state_changes
);
463 g_atomic_int_dec_and_test (&ignore_state_changes
);
464 release_notifications ();
469 Playlist::delay_notifications ()
471 g_atomic_int_inc (&block_notifications
);
472 freeze_length
= _get_extent().second
;
476 Playlist::release_notifications ()
478 if (g_atomic_int_dec_and_test (&block_notifications
)) {
479 flush_notifications ();
485 Playlist::notify_contents_changed ()
487 if (holding_state ()) {
488 pending_contents_change
= true;
490 pending_contents_change
= false;
491 ContentsChanged(); /* EMIT SIGNAL */
496 Playlist::notify_layering_changed ()
498 if (holding_state ()) {
499 pending_layering
= true;
501 pending_layering
= false;
502 LayeringChanged(); /* EMIT SIGNAL */
507 Playlist::notify_region_removed (boost::shared_ptr
<Region
> r
)
509 if (holding_state ()) {
510 pending_removes
.insert (r
);
511 pending_contents_change
= true;
512 pending_length
= true;
514 /* this might not be true, but we have to act
515 as though it could be.
517 pending_length
= false;
518 LengthChanged (); /* EMIT SIGNAL */
519 pending_contents_change
= false;
520 RegionRemoved (boost::weak_ptr
<Region
> (r
)); /* EMIT SIGNAL */
521 ContentsChanged (); /* EMIT SIGNAL */
526 Playlist::notify_region_moved (boost::shared_ptr
<Region
> r
)
528 Evoral::RangeMove
<framepos_t
> const move (r
->last_position (), r
->length (), r
->position ());
530 if (holding_state ()) {
532 pending_range_moves
.push_back (move
);
536 list
< Evoral::RangeMove
<framepos_t
> > m
;
544 Playlist::notify_region_added (boost::shared_ptr
<Region
> r
)
546 /* the length change might not be true, but we have to act
547 as though it could be.
550 if (holding_state()) {
551 pending_adds
.insert (r
);
552 pending_contents_change
= true;
553 pending_length
= true;
556 pending_length
= false;
557 LengthChanged (); /* EMIT SIGNAL */
558 pending_contents_change
= false;
559 RegionAdded (boost::weak_ptr
<Region
> (r
)); /* EMIT SIGNAL */
560 ContentsChanged (); /* EMIT SIGNAL */
565 Playlist::notify_length_changed ()
567 if (holding_state ()) {
568 pending_length
= true;
570 pending_length
= false;
571 LengthChanged(); /* EMIT SIGNAL */
572 pending_contents_change
= false;
573 ContentsChanged (); /* EMIT SIGNAL */
578 Playlist::flush_notifications ()
580 set
<boost::shared_ptr
<Region
> > dependent_checks_needed
;
581 set
<boost::shared_ptr
<Region
> >::iterator s
;
582 uint32_t regions_changed
= false;
583 bool check_length
= false;
584 framecnt_t old_length
= 0;
592 if (!pending_bounds
.empty() || !pending_removes
.empty() || !pending_adds
.empty()) {
593 regions_changed
= true;
594 if (!pending_length
) {
595 old_length
= _get_extent ().second
;
600 /* we have no idea what order the regions ended up in pending
601 bounds (it could be based on selection order, for example).
602 so, to preserve layering in the "most recently moved is higher"
603 model, sort them by existing layer, then timestamp them.
606 // RegionSortByLayer cmp;
607 // pending_bounds.sort (cmp);
609 for (RegionList::iterator r
= pending_bounds
.begin(); r
!= pending_bounds
.end(); ++r
) {
610 if (_session
.config
.get_layer_model() == MoveAddHigher
) {
611 timestamp_layer_op (*r
);
613 dependent_checks_needed
.insert (*r
);
616 for (s
= pending_removes
.begin(); s
!= pending_removes
.end(); ++s
) {
617 remove_dependents (*s
);
618 // cerr << _name << " sends RegionRemoved\n";
619 RegionRemoved (boost::weak_ptr
<Region
> (*s
)); /* EMIT SIGNAL */
622 for (s
= pending_adds
.begin(); s
!= pending_adds
.end(); ++s
) {
623 // cerr << _name << " sends RegionAdded\n";
624 /* don't emit RegionAdded signal until relayering is done,
625 so that the region is fully setup by the time
626 anyone hear's that its been added
628 dependent_checks_needed
.insert (*s
);
632 if (old_length
!= _get_extent().second
) {
633 pending_length
= true;
634 // cerr << _name << " length has changed\n";
638 if (pending_length
|| (freeze_length
!= _get_extent().second
)) {
639 pending_length
= false;
640 // cerr << _name << " sends LengthChanged\n";
641 LengthChanged(); /* EMIT SIGNAL */
644 if (regions_changed
|| pending_contents_change
) {
648 pending_contents_change
= false;
649 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
650 ContentsChanged (); /* EMIT SIGNAL */
651 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
654 for (s
= pending_adds
.begin(); s
!= pending_adds
.end(); ++s
) {
655 (*s
)->clear_history ();
656 RegionAdded (boost::weak_ptr
<Region
> (*s
)); /* EMIT SIGNAL */
659 for (s
= dependent_checks_needed
.begin(); s
!= dependent_checks_needed
.end(); ++s
) {
660 check_dependents (*s
, false);
663 if (!pending_range_moves
.empty ()) {
664 // cerr << _name << " sends RangesMoved\n";
665 RangesMoved (pending_range_moves
);
674 Playlist::clear_pending ()
676 pending_adds
.clear ();
677 pending_removes
.clear ();
678 pending_bounds
.clear ();
679 pending_range_moves
.clear ();
680 pending_contents_change
= false;
681 pending_length
= false;
684 /*************************************************************
686 *************************************************************/
689 Playlist::add_region (boost::shared_ptr
<Region
> region
, framepos_t position
, float times
, bool auto_partition
)
691 RegionLock
rlock (this);
692 times
= fabs (times
);
694 int itimes
= (int) floor (times
);
696 framepos_t pos
= position
;
698 if (times
== 1 && auto_partition
){
699 partition(pos
, (pos
+ region
->length()), true);
703 add_region_internal (region
, pos
);
704 pos
+= region
->length();
709 /* note that itimes can be zero if we being asked to just
710 insert a single fraction of the region.
713 for (int i
= 0; i
< itimes
; ++i
) {
714 boost::shared_ptr
<Region
> copy
= RegionFactory::create (region
);
715 add_region_internal (copy
, pos
);
716 pos
+= region
->length();
719 framecnt_t length
= 0;
721 if (floor (times
) != times
) {
722 length
= (framecnt_t
) floor (region
->length() * (times
- floor (times
)));
724 RegionFactory::region_name (name
, region
->name(), false);
729 plist
.add (Properties::start
, region
->start());
730 plist
.add (Properties::length
, length
);
731 plist
.add (Properties::name
, name
);
732 plist
.add (Properties::layer
, region
->layer());
734 boost::shared_ptr
<Region
> sub
= RegionFactory::create (region
, plist
);
735 add_region_internal (sub
, pos
);
739 possibly_splice_unlocked (position
, (pos
+ length
) - position
, boost::shared_ptr
<Region
>());
743 Playlist::set_region_ownership ()
745 RegionLock
rl (this);
746 RegionList::iterator i
;
747 boost::weak_ptr
<Playlist
> pl (shared_from_this());
749 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
750 (*i
)->set_playlist (pl
);
755 Playlist::add_region_internal (boost::shared_ptr
<Region
> region
, framepos_t position
)
757 if (region
->data_type() != _type
){
761 RegionSortByPosition cmp
;
763 framecnt_t old_length
= 0;
765 if (!holding_state()) {
766 old_length
= _get_extent().second
;
769 if (!first_set_state
) {
770 boost::shared_ptr
<Playlist
> foo (shared_from_this());
771 region
->set_playlist (boost::weak_ptr
<Playlist
>(foo
));
774 region
->set_position (position
, this);
776 timestamp_layer_op (region
);
778 regions
.insert (upper_bound (regions
.begin(), regions
.end(), region
, cmp
), region
);
779 all_regions
.insert (region
);
781 possibly_splice_unlocked (position
, region
->length(), region
);
783 if (!holding_state ()) {
784 /* layers get assigned from XML state, and are not reset during undo/redo */
788 /* we need to notify the existence of new region before checking dependents. Ick. */
790 notify_region_added (region
);
792 if (!holding_state ()) {
794 check_dependents (region
, false);
796 if (old_length
!= _get_extent().second
) {
797 notify_length_changed ();
801 region
->PropertyChanged
.connect_same_thread (region_state_changed_connections
, boost::bind (&Playlist::region_changed_proxy
, this, _1
, boost::weak_ptr
<Region
> (region
)));
807 Playlist::replace_region (boost::shared_ptr
<Region
> old
, boost::shared_ptr
<Region
> newr
, framepos_t pos
)
809 RegionLock
rlock (this);
811 bool old_sp
= _splicing
;
814 remove_region_internal (old
);
815 add_region_internal (newr
, pos
);
819 possibly_splice_unlocked (pos
, old
->length() - newr
->length());
823 Playlist::remove_region (boost::shared_ptr
<Region
> region
)
825 RegionLock
rlock (this);
826 remove_region_internal (region
);
830 Playlist::remove_region_internal (boost::shared_ptr
<Region
> region
)
832 RegionList::iterator i
;
833 framecnt_t old_length
= 0;
836 if (!holding_state()) {
837 old_length
= _get_extent().second
;
842 region
->set_playlist (boost::weak_ptr
<Playlist
>());
845 /* XXX should probably freeze here .... */
847 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
850 framepos_t pos
= (*i
)->position();
851 framecnt_t distance
= (*i
)->length();
855 possibly_splice_unlocked (pos
, -distance
);
857 if (!holding_state ()) {
859 remove_dependents (region
);
861 if (old_length
!= _get_extent().second
) {
862 notify_length_changed ();
866 notify_region_removed (region
);
872 /* XXX and thaw ... */
878 Playlist::get_equivalent_regions (boost::shared_ptr
<Region
> other
, vector
<boost::shared_ptr
<Region
> >& results
)
880 if (Config
->get_use_overlap_equivalency()) {
881 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
882 if ((*i
)->overlap_equivalent (other
)) {
883 results
.push_back ((*i
));
887 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
888 if ((*i
)->equivalent (other
)) {
889 results
.push_back ((*i
));
896 Playlist::get_region_list_equivalent_regions (boost::shared_ptr
<Region
> other
, vector
<boost::shared_ptr
<Region
> >& results
)
898 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
900 if ((*i
) && (*i
)->region_list_equivalent (other
)) {
901 results
.push_back (*i
);
907 Playlist::partition (framepos_t start
, framepos_t end
, bool cut
)
911 partition_internal (start
, end
, cut
, thawlist
);
913 for (RegionList::iterator i
= thawlist
.begin(); i
!= thawlist
.end(); ++i
) {
914 (*i
)->resume_property_changes ();
919 Playlist::partition_internal (framepos_t start
, framepos_t end
, bool cutting
, RegionList
& thawlist
)
921 RegionList new_regions
;
924 RegionLock
rlock (this);
926 boost::shared_ptr
<Region
> region
;
927 boost::shared_ptr
<Region
> current
;
929 RegionList::iterator tmp
;
931 framepos_t pos1
, pos2
, pos3
, pos4
;
935 /* need to work from a copy, because otherwise the regions we add during the process
936 get operated on as well.
939 RegionList copy
= regions
.rlist();
941 for (RegionList::iterator i
= copy
.begin(); i
!= copy
.end(); i
= tmp
) {
948 if (current
->first_frame() >= start
&& current
->last_frame() < end
) {
951 remove_region_internal (current
);
957 /* coverage will return OverlapStart if the start coincides
958 with the end point. we do not partition such a region,
959 so catch this special case.
962 if (current
->first_frame() >= end
) {
966 if ((overlap
= current
->coverage (start
, end
)) == OverlapNone
) {
970 pos1
= current
->position();
973 pos4
= current
->last_frame();
975 if (overlap
== OverlapInternal
) {
976 /* split: we need 3 new regions, the front, middle and end.
977 cut: we need 2 regions, the front and end.
982 ---------------*************************------------
985 ---------------*****++++++++++++++++====------------
987 ---------------*****----------------====------------
992 /* "middle" ++++++ */
994 RegionFactory::region_name (new_name
, current
->name(), false);
998 plist
.add (Properties::start
, current
->start() + (pos2
- pos1
));
999 plist
.add (Properties::length
, pos3
- pos2
);
1000 plist
.add (Properties::name
, new_name
);
1001 plist
.add (Properties::layer
, regions
.size());
1002 plist
.add (Properties::automatic
, true);
1003 plist
.add (Properties::left_of_split
, true);
1004 plist
.add (Properties::right_of_split
, true);
1006 region
= RegionFactory::create (current
, plist
);
1007 add_region_internal (region
, start
);
1008 new_regions
.push_back (region
);
1013 RegionFactory::region_name (new_name
, current
->name(), false);
1017 plist
.add (Properties::start
, current
->start() + (pos3
- pos1
));
1018 plist
.add (Properties::length
, pos4
- pos3
);
1019 plist
.add (Properties::name
, new_name
);
1020 plist
.add (Properties::layer
, regions
.size());
1021 plist
.add (Properties::automatic
, true);
1022 plist
.add (Properties::right_of_split
, true);
1024 region
= RegionFactory::create (current
, plist
);
1026 add_region_internal (region
, end
);
1027 new_regions
.push_back (region
);
1031 current
->suspend_property_changes ();
1032 thawlist
.push_back (current
);
1033 current
->cut_end (pos2
- 1, this);
1035 } else if (overlap
== OverlapEnd
) {
1039 ---------------*************************------------
1042 ---------------**************+++++++++++------------
1044 ---------------**************-----------------------
1051 RegionFactory::region_name (new_name
, current
->name(), false);
1055 plist
.add (Properties::start
, current
->start() + (pos2
- pos1
));
1056 plist
.add (Properties::length
, pos4
- pos2
);
1057 plist
.add (Properties::name
, new_name
);
1058 plist
.add (Properties::layer
, regions
.size());
1059 plist
.add (Properties::automatic
, true);
1060 plist
.add (Properties::left_of_split
, true);
1062 region
= RegionFactory::create (current
, plist
);
1064 add_region_internal (region
, start
);
1065 new_regions
.push_back (region
);
1070 current
->suspend_property_changes ();
1071 thawlist
.push_back (current
);
1072 current
->cut_end (pos2
- 1, this);
1074 } else if (overlap
== OverlapStart
) {
1076 /* split: we need 2 regions: the front and the end.
1077 cut: just trim current to skip the cut area
1082 ---------------*************************------------
1086 ---------------****+++++++++++++++++++++------------
1088 -------------------*********************------------
1094 RegionFactory::region_name (new_name
, current
->name(), false);
1098 plist
.add (Properties::start
, current
->start());
1099 plist
.add (Properties::length
, pos3
- pos1
);
1100 plist
.add (Properties::name
, new_name
);
1101 plist
.add (Properties::layer
, regions
.size());
1102 plist
.add (Properties::automatic
, true);
1103 plist
.add (Properties::right_of_split
, true);
1105 region
= RegionFactory::create (current
, plist
);
1107 add_region_internal (region
, pos1
);
1108 new_regions
.push_back (region
);
1113 current
->suspend_property_changes ();
1114 thawlist
.push_back (current
);
1115 current
->trim_front (pos3
, this);
1116 } else if (overlap
== OverlapExternal
) {
1118 /* split: no split required.
1119 cut: remove the region.
1124 ---------------*************************------------
1128 ---------------*************************------------
1130 ----------------------------------------------------
1135 remove_region_internal (current
);
1138 new_regions
.push_back (current
);
1142 in_partition
= false;
1145 for (RegionList::iterator i
= new_regions
.begin(); i
!= new_regions
.end(); ++i
) {
1146 check_dependents (*i
, false);
1150 boost::shared_ptr
<Playlist
>
1151 Playlist::cut_copy (boost::shared_ptr
<Playlist
> (Playlist::*pmf
)(framepos_t
, framecnt_t
,bool), list
<AudioRange
>& ranges
, bool result_is_hidden
)
1153 boost::shared_ptr
<Playlist
> ret
;
1154 boost::shared_ptr
<Playlist
> pl
;
1157 if (ranges
.empty()) {
1158 return boost::shared_ptr
<Playlist
>();
1161 start
= ranges
.front().start
;
1163 for (list
<AudioRange
>::iterator i
= ranges
.begin(); i
!= ranges
.end(); ++i
) {
1165 pl
= (this->*pmf
)((*i
).start
, (*i
).length(), result_is_hidden
);
1167 if (i
== ranges
.begin()) {
1171 /* paste the next section into the nascent playlist,
1172 offset to reflect the start of the first range we
1176 ret
->paste (pl
, (*i
).start
- start
, 1.0f
);
1183 boost::shared_ptr
<Playlist
>
1184 Playlist::cut (list
<AudioRange
>& ranges
, bool result_is_hidden
)
1186 boost::shared_ptr
<Playlist
> (Playlist::*pmf
)(framepos_t
,framecnt_t
,bool) = &Playlist::cut
;
1187 return cut_copy (pmf
, ranges
, result_is_hidden
);
1190 boost::shared_ptr
<Playlist
>
1191 Playlist::copy (list
<AudioRange
>& ranges
, bool result_is_hidden
)
1193 boost::shared_ptr
<Playlist
> (Playlist::*pmf
)(framepos_t
,framecnt_t
,bool) = &Playlist::copy
;
1194 return cut_copy (pmf
, ranges
, result_is_hidden
);
1197 boost::shared_ptr
<Playlist
>
1198 Playlist::cut (framepos_t start
, framecnt_t cnt
, bool result_is_hidden
)
1200 boost::shared_ptr
<Playlist
> the_copy
;
1201 RegionList thawlist
;
1204 snprintf (buf
, sizeof (buf
), "%" PRIu32
, ++subcnt
);
1205 string new_name
= _name
;
1209 if ((the_copy
= PlaylistFactory::create (shared_from_this(), start
, cnt
, new_name
, result_is_hidden
)) == 0) {
1210 return boost::shared_ptr
<Playlist
>();
1213 partition_internal (start
, start
+cnt
-1, true, thawlist
);
1215 for (RegionList::iterator i
= thawlist
.begin(); i
!= thawlist
.end(); ++i
) {
1216 (*i
)->resume_property_changes();
1222 boost::shared_ptr
<Playlist
>
1223 Playlist::copy (framepos_t start
, framecnt_t cnt
, bool result_is_hidden
)
1227 snprintf (buf
, sizeof (buf
), "%" PRIu32
, ++subcnt
);
1228 string new_name
= _name
;
1232 cnt
= min (_get_extent().second
- start
, cnt
);
1233 return PlaylistFactory::create (shared_from_this(), start
, cnt
, new_name
, result_is_hidden
);
1237 Playlist::paste (boost::shared_ptr
<Playlist
> other
, framepos_t position
, float times
)
1239 times
= fabs (times
);
1242 RegionLock
rl1 (this);
1243 RegionLock
rl2 (other
.get());
1245 framecnt_t
const old_length
= _get_extent().second
;
1247 int itimes
= (int) floor (times
);
1248 framepos_t pos
= position
;
1249 framecnt_t
const shift
= other
->_get_extent().second
;
1250 layer_t top_layer
= regions
.size();
1253 for (RegionList::iterator i
= other
->regions
.begin(); i
!= other
->regions
.end(); ++i
) {
1254 boost::shared_ptr
<Region
> copy_of_region
= RegionFactory::create (*i
);
1256 /* put these new regions on top of all existing ones, but preserve
1257 the ordering they had in the original playlist.
1260 copy_of_region
->set_layer (copy_of_region
->layer() + top_layer
);
1261 add_region_internal (copy_of_region
, copy_of_region
->position() + pos
);
1267 /* XXX shall we handle fractional cases at some point? */
1269 if (old_length
!= _get_extent().second
) {
1270 notify_length_changed ();
1281 Playlist::duplicate (boost::shared_ptr
<Region
> region
, framepos_t position
, float times
)
1283 times
= fabs (times
);
1285 RegionLock
rl (this);
1286 int itimes
= (int) floor (times
);
1287 framepos_t pos
= position
;
1290 boost::shared_ptr
<Region
> copy
= RegionFactory::create (region
);
1291 add_region_internal (copy
, pos
);
1292 pos
+= region
->length();
1295 if (floor (times
) != times
) {
1296 framecnt_t length
= (framecnt_t
) floor (region
->length() * (times
- floor (times
)));
1298 RegionFactory::region_name (name
, region
->name(), false);
1303 plist
.add (Properties::start
, region
->start());
1304 plist
.add (Properties::length
, length
);
1305 plist
.add (Properties::name
, name
);
1307 boost::shared_ptr
<Region
> sub
= RegionFactory::create (region
, plist
);
1308 add_region_internal (sub
, pos
);
1314 Playlist::shift (framepos_t at
, frameoffset_t distance
, bool move_intersected
, bool ignore_music_glue
)
1316 RegionLock
rlock (this);
1317 RegionList
copy (regions
.rlist());
1320 for (RegionList::iterator r
= copy
.begin(); r
!= copy
.end(); ++r
) {
1322 if ((*r
)->last_frame() < at
) {
1327 if (at
> (*r
)->first_frame() && at
< (*r
)->last_frame()) {
1328 /* intersected region */
1329 if (!move_intersected
) {
1334 /* do not move regions glued to music time - that
1335 has to be done separately.
1338 if (!ignore_music_glue
&& (*r
)->position_lock_style() != AudioTime
) {
1339 fixup
.push_back (*r
);
1343 (*r
)->set_position ((*r
)->position() + distance
, this);
1346 for (RegionList::iterator r
= fixup
.begin(); r
!= fixup
.end(); ++r
) {
1347 (*r
)->recompute_position_from_lock_style ();
1352 Playlist::split (framepos_t at
)
1354 RegionLock
rlock (this);
1355 RegionList
copy (regions
.rlist());
1357 /* use a copy since this operation can modify the region list
1360 for (RegionList::iterator r
= copy
.begin(); r
!= copy
.end(); ++r
) {
1361 _split_region (*r
, at
);
1366 Playlist::split_region (boost::shared_ptr
<Region
> region
, framepos_t playlist_position
)
1368 RegionLock
rl (this);
1369 _split_region (region
, playlist_position
);
1373 Playlist::_split_region (boost::shared_ptr
<Region
> region
, framepos_t playlist_position
)
1375 if (!region
->covers (playlist_position
)) {
1379 if (region
->position() == playlist_position
||
1380 region
->last_frame() == playlist_position
) {
1384 boost::shared_ptr
<Region
> left
;
1385 boost::shared_ptr
<Region
> right
;
1386 frameoffset_t before
;
1387 frameoffset_t after
;
1391 /* split doesn't change anything about length, so don't try to splice */
1393 bool old_sp
= _splicing
;
1396 before
= playlist_position
- region
->position();
1397 after
= region
->length() - before
;
1399 RegionFactory::region_name (before_name
, region
->name(), false);
1404 plist
.add (Properties::start
, region
->start());
1405 plist
.add (Properties::length
, before
);
1406 plist
.add (Properties::name
, before_name
);
1407 plist
.add (Properties::left_of_split
, true);
1409 left
= RegionFactory::create (region
, plist
);
1412 RegionFactory::region_name (after_name
, region
->name(), false);
1417 plist
.add (Properties::start
, region
->start() + before
);
1418 plist
.add (Properties::length
, after
);
1419 plist
.add (Properties::name
, after_name
);
1420 plist
.add (Properties::right_of_split
, true);
1422 right
= RegionFactory::create (region
, plist
);
1425 add_region_internal (left
, region
->position());
1426 add_region_internal (right
, region
->position() + before
);
1428 uint64_t orig_layer_op
= region
->last_layer_op();
1429 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1430 if ((*i
)->last_layer_op() > orig_layer_op
) {
1431 (*i
)->set_last_layer_op( (*i
)->last_layer_op() + 1 );
1435 left
->set_last_layer_op ( orig_layer_op
);
1436 right
->set_last_layer_op ( orig_layer_op
+ 1);
1440 finalize_split_region (region
, left
, right
);
1442 remove_region_internal (region
);
1448 Playlist::possibly_splice (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1450 if (_splicing
|| in_set_state
) {
1451 /* don't respond to splicing moves or state setting */
1455 if (_edit_mode
== Splice
) {
1456 splice_locked (at
, distance
, exclude
);
1461 Playlist::possibly_splice_unlocked (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1463 if (_splicing
|| in_set_state
) {
1464 /* don't respond to splicing moves or state setting */
1468 if (_edit_mode
== Splice
) {
1469 splice_unlocked (at
, distance
, exclude
);
1474 Playlist::splice_locked (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1477 RegionLock
rl (this);
1478 core_splice (at
, distance
, exclude
);
1483 Playlist::splice_unlocked (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1485 core_splice (at
, distance
, exclude
);
1489 Playlist::core_splice (framepos_t at
, framecnt_t distance
, boost::shared_ptr
<Region
> exclude
)
1493 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1495 if (exclude
&& (*i
) == exclude
) {
1499 if ((*i
)->position() >= at
) {
1500 framepos_t new_pos
= (*i
)->position() + distance
;
1503 } else if (new_pos
>= max_frames
- (*i
)->length()) {
1504 new_pos
= max_frames
- (*i
)->length();
1507 (*i
)->set_position (new_pos
, this);
1513 notify_length_changed ();
1517 Playlist::region_bounds_changed (const PropertyChange
& what_changed
, boost::shared_ptr
<Region
> region
)
1519 if (in_set_state
|| _splicing
|| _nudging
|| _shuffling
) {
1523 if (what_changed
.contains (Properties::position
)) {
1525 /* remove it from the list then add it back in
1526 the right place again.
1529 RegionSortByPosition cmp
;
1531 RegionList::iterator i
= find (regions
.begin(), regions
.end(), region
);
1533 if (i
== regions
.end()) {
1534 /* the region bounds are being modified but its not currently
1535 in the region list. we will use its bounds correctly when/if
1542 regions
.insert (upper_bound (regions
.begin(), regions
.end(), region
, cmp
), region
);
1545 if (what_changed
.contains (Properties::position
) || what_changed
.contains (Properties::length
)) {
1547 frameoffset_t delta
= 0;
1549 if (what_changed
.contains (Properties::position
)) {
1550 delta
= region
->position() - region
->last_position();
1553 if (what_changed
.contains (Properties::length
)) {
1554 delta
+= region
->length() - region
->last_length();
1558 possibly_splice (region
->last_position() + region
->last_length(), delta
, region
);
1561 if (holding_state ()) {
1562 pending_bounds
.push_back (region
);
1564 if (_session
.config
.get_layer_model() == MoveAddHigher
) {
1565 /* it moved or changed length, so change the timestamp */
1566 timestamp_layer_op (region
);
1569 notify_length_changed ();
1571 check_dependents (region
, false);
1577 Playlist::region_changed_proxy (const PropertyChange
& what_changed
, boost::weak_ptr
<Region
> weak_region
)
1579 boost::shared_ptr
<Region
> region (weak_region
.lock());
1585 /* this makes a virtual call to the right kind of playlist ... */
1587 region_changed (what_changed
, region
);
1591 Playlist::region_changed (const PropertyChange
& what_changed
, boost::shared_ptr
<Region
> region
)
1593 PropertyChange our_interests
;
1594 PropertyChange bounds
;
1595 PropertyChange pos_and_length
;
1598 if (in_set_state
|| in_flush
) {
1602 our_interests
.add (Properties::muted
);
1603 our_interests
.add (Properties::layer
);
1604 our_interests
.add (Properties::opaque
);
1606 bounds
.add (Properties::start
);
1607 bounds
.add (Properties::position
);
1608 bounds
.add (Properties::length
);
1610 pos_and_length
.add (Properties::position
);
1611 pos_and_length
.add (Properties::length
);
1613 if (what_changed
.contains (bounds
)) {
1614 region_bounds_changed (what_changed
, region
);
1615 save
= !(_splicing
|| _nudging
);
1618 if (what_changed
.contains (our_interests
) && !what_changed
.contains (pos_and_length
)) {
1619 check_dependents (region
, false);
1622 if (what_changed
.contains (Properties::position
)) {
1623 notify_region_moved (region
);
1627 /* don't notify about layer changes, since we are the only object that can initiate
1628 them, and we notify in ::relayer()
1631 if (what_changed
.contains (our_interests
)) {
1639 Playlist::drop_regions ()
1641 RegionLock
rl (this);
1643 all_regions
.clear ();
1647 Playlist::clear (bool with_signals
)
1650 RegionLock
rl (this);
1652 region_state_changed_connections
.drop_connections ();
1654 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1655 pending_removes
.insert (*i
);
1660 for (set
<boost::shared_ptr
<Region
> >::iterator s
= pending_removes
.begin(); s
!= pending_removes
.end(); ++s
) {
1661 remove_dependents (*s
);
1667 for (set
<boost::shared_ptr
<Region
> >::iterator s
= pending_removes
.begin(); s
!= pending_removes
.end(); ++s
) {
1668 RegionRemoved (boost::weak_ptr
<Region
> (*s
)); /* EMIT SIGNAL */
1671 pending_removes
.clear ();
1672 pending_length
= false;
1674 pending_contents_change
= false;
1680 /***********************************************************************
1682 **********************************************************************/
1684 Playlist::RegionList
*
1685 Playlist::regions_at (framepos_t frame
)
1688 RegionLock
rlock (this);
1689 return find_regions_at (frame
);
1692 boost::shared_ptr
<Region
>
1693 Playlist::top_region_at (framepos_t frame
)
1696 RegionLock
rlock (this);
1697 RegionList
*rlist
= find_regions_at (frame
);
1698 boost::shared_ptr
<Region
> region
;
1700 if (rlist
->size()) {
1701 RegionSortByLayer cmp
;
1703 region
= rlist
->back();
1710 boost::shared_ptr
<Region
>
1711 Playlist::top_unmuted_region_at (framepos_t frame
)
1714 RegionLock
rlock (this);
1715 RegionList
*rlist
= find_regions_at (frame
);
1717 for (RegionList::iterator i
= rlist
->begin(); i
!= rlist
->end(); ) {
1719 RegionList::iterator tmp
= i
;
1722 if ((*i
)->muted()) {
1729 boost::shared_ptr
<Region
> region
;
1731 if (rlist
->size()) {
1732 RegionSortByLayer cmp
;
1734 region
= rlist
->back();
1741 Playlist::RegionList
*
1742 Playlist::regions_to_read (framepos_t start
, framepos_t end
)
1744 /* Caller must hold lock */
1746 RegionList covering
;
1747 set
<framepos_t
> to_check
;
1748 set
<boost::shared_ptr
<Region
> > unique
;
1750 to_check
.insert (start
);
1751 to_check
.insert (end
);
1753 DEBUG_TRACE (DEBUG::AudioPlayback
, ">>>>> REGIONS TO READ\n");
1755 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1757 /* find all/any regions that span start+end */
1759 switch ((*i
)->coverage (start
, end
)) {
1763 case OverlapInternal
:
1764 covering
.push_back (*i
);
1765 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("toread: will cover %1 (OInternal)\n", (*i
)->name()));
1769 to_check
.insert ((*i
)->position());
1770 if ((*i
)->position() != 0) {
1771 to_check
.insert ((*i
)->position()-1);
1773 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("toread: will check %1 for %2\n", (*i
)->position(), (*i
)->name()));
1774 covering
.push_back (*i
);
1778 to_check
.insert ((*i
)->last_frame());
1779 to_check
.insert ((*i
)->last_frame()+1);
1780 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("toread: will cover %1 (OEnd)\n", (*i
)->name()));
1781 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("\ttoread: will check %1 for %2\n", (*i
)->last_frame(), (*i
)->name()));
1782 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("\ttoread: will check %1 for %2\n", (*i
)->last_frame(), (*i
)->name()));
1783 covering
.push_back (*i
);
1786 case OverlapExternal
:
1787 covering
.push_back (*i
);
1788 to_check
.insert ((*i
)->position());
1789 if ((*i
)->position() != 0) {
1790 to_check
.insert ((*i
)->position()-1);
1792 to_check
.insert ((*i
)->last_frame());
1793 to_check
.insert ((*i
)->last_frame()+1);
1794 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("toread: will cover %1 (OExt)\n", (*i
)->name()));
1795 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("\ttoread: will check %1 for %2\n", (*i
)->position(), (*i
)->name()));
1796 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("\ttoread: will check %1 for %2\n", (*i
)->last_frame(), (*i
)->name()));
1800 /* don't go too far */
1802 if ((*i
)->position() > end
) {
1807 RegionList
* rlist
= new RegionList
;
1809 /* find all the regions that cover each position .... */
1811 if (covering
.size() == 1) {
1813 rlist
->push_back (covering
.front());
1814 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("Just one covering region (%1)\n", covering
.front()->name()));
1819 for (set
<framepos_t
>::iterator t
= to_check
.begin(); t
!= to_check
.end(); ++t
) {
1823 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("++++ Considering %1\n", *t
));
1825 for (RegionList::iterator x
= covering
.begin(); x
!= covering
.end(); ++x
) {
1827 if ((*x
)->covers (*t
)) {
1828 here
.push_back (*x
);
1829 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("region %1 covers %2\n",
1833 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("region %1 does NOT covers %2\n",
1840 RegionSortByLayer cmp
;
1843 /* ... and get the top/transparent regions at "here" */
1845 for (RegionList::reverse_iterator c
= here
.rbegin(); c
!= here
.rend(); ++c
) {
1849 if ((*c
)->opaque()) {
1851 /* the other regions at this position are hidden by this one */
1852 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("%1 is opaque, ignore all others\n",
1859 for (set
<boost::shared_ptr
<Region
> >::iterator s
= unique
.begin(); s
!= unique
.end(); ++s
) {
1860 rlist
->push_back (*s
);
1863 if (rlist
->size() > 1) {
1864 /* now sort by time order */
1866 RegionSortByPosition cmp
;
1871 DEBUG_TRACE (DEBUG::AudioPlayback
, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist
->size()));
1876 Playlist::RegionList
*
1877 Playlist::find_regions_at (framepos_t frame
)
1879 /* Caller must hold lock */
1881 RegionList
*rlist
= new RegionList
;
1883 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1884 if ((*i
)->covers (frame
)) {
1885 rlist
->push_back (*i
);
1892 Playlist::RegionList
*
1893 Playlist::regions_touched (framepos_t start
, framepos_t end
)
1895 RegionLock
rlock (this);
1896 RegionList
*rlist
= new RegionList
;
1898 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1899 if ((*i
)->coverage (start
, end
) != OverlapNone
) {
1900 rlist
->push_back (*i
);
1908 Playlist::find_next_transient (framepos_t from
, int dir
)
1910 RegionLock
rlock (this);
1911 AnalysisFeatureList points
;
1912 AnalysisFeatureList these_points
;
1914 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1916 if ((*i
)->last_frame() < from
) {
1920 if ((*i
)->first_frame() > from
) {
1925 (*i
)->get_transients (these_points
);
1927 /* add first frame, just, err, because */
1929 these_points
.push_back ((*i
)->first_frame());
1931 points
.insert (points
.end(), these_points
.begin(), these_points
.end());
1932 these_points
.clear ();
1935 if (points
.empty()) {
1939 TransientDetector::cleanup_transients (points
, _session
.frame_rate(), 3.0);
1940 bool reached
= false;
1943 for (AnalysisFeatureList::iterator x
= points
.begin(); x
!= points
.end(); ++x
) {
1948 if (reached
&& (*x
) > from
) {
1953 for (AnalysisFeatureList::reverse_iterator x
= points
.rbegin(); x
!= points
.rend(); ++x
) {
1958 if (reached
&& (*x
) < from
) {
1967 boost::shared_ptr
<Region
>
1968 Playlist::find_next_region (framepos_t frame
, RegionPoint point
, int dir
)
1970 RegionLock
rlock (this);
1971 boost::shared_ptr
<Region
> ret
;
1972 framepos_t closest
= max_frames
;
1974 bool end_iter
= false;
1976 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
1980 frameoffset_t distance
;
1981 boost::shared_ptr
<Region
> r
= (*i
);
1986 pos
= r
->first_frame ();
1989 pos
= r
->last_frame ();
1992 pos
= r
->sync_position ();
1993 // r->adjust_to_sync (r->first_frame());
1998 case 1: /* forwards */
2001 if ((distance
= pos
- frame
) < closest
) {
2010 default: /* backwards */
2013 if ((distance
= frame
- pos
) < closest
) {
2030 Playlist::find_next_region_boundary (framepos_t frame
, int dir
)
2032 RegionLock
rlock (this);
2034 framepos_t closest
= max_frames
;
2035 framepos_t ret
= -1;
2039 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2041 boost::shared_ptr
<Region
> r
= (*i
);
2042 frameoffset_t distance
;
2044 if (r
->first_frame() > frame
) {
2046 distance
= r
->first_frame() - frame
;
2048 if (distance
< closest
) {
2049 ret
= r
->first_frame();
2054 if (r
->last_frame () > frame
) {
2056 distance
= r
->last_frame () - frame
;
2058 if (distance
< closest
) {
2059 ret
= r
->last_frame ();
2067 for (RegionList::reverse_iterator i
= regions
.rbegin(); i
!= regions
.rend(); ++i
) {
2069 boost::shared_ptr
<Region
> r
= (*i
);
2070 frameoffset_t distance
;
2072 if (r
->last_frame() < frame
) {
2074 distance
= frame
- r
->last_frame();
2076 if (distance
< closest
) {
2077 ret
= r
->last_frame();
2082 if (r
->first_frame() < frame
) {
2084 distance
= frame
- r
->first_frame();
2086 if (distance
< closest
) {
2087 ret
= r
->first_frame();
2097 /***********************************************************************/
2103 Playlist::mark_session_dirty ()
2105 if (!in_set_state
&& !holding_state ()) {
2106 _session
.set_dirty();
2111 Playlist::set_property (const PropertyBase
& prop
)
2113 if (prop
== Properties::regions
.property_id
) {
2114 const RegionListProperty::ChangeRecord
& change (dynamic_cast<const RegionListProperty
*>(&prop
)->change());
2115 regions
.update (change
);
2116 return (!change
.added
.empty() && !change
.removed
.empty());
2122 Playlist::rdiff (vector
<StatefulDiffCommand
*>& cmds
) const
2124 RegionLock
rlock (const_cast<Playlist
*> (this));
2126 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2127 if ((*i
)->changed ()) {
2128 StatefulDiffCommand
* sdc
= new StatefulDiffCommand (*i
);
2129 cmds
.push_back (sdc
);
2135 Playlist::clear_owned_history ()
2137 RegionLock
rlock (this);
2139 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2140 (*i
)->clear_history ();
2145 Playlist::update (const RegionListProperty::ChangeRecord
& change
)
2147 DEBUG_TRACE (DEBUG::Properties
, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2148 name(), change
.added
.size(), change
.removed
.size()));
2151 /* add the added regions */
2152 for (RegionListProperty::ChangeContainer::iterator i
= change
.added
.begin(); i
!= change
.added
.end(); ++i
) {
2153 add_region ((*i
), (*i
)->position());
2155 /* remove the removed regions */
2156 for (RegionListProperty::ChangeContainer::iterator i
= change
.removed
.begin(); i
!= change
.removed
.end(); ++i
) {
2164 Playlist::property_factory (const XMLNode
& history_node
) const
2166 const XMLNodeList
& children (history_node
.children());
2167 PropertyList
* prop_list
= 0;
2169 for (XMLNodeList::const_iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
2171 if ((*i
)->name() == capitalize (regions
.property_name())) {
2173 RegionListProperty
* rlp
= new RegionListProperty (*const_cast<Playlist
*> (this));
2175 if (rlp
->load_history_state (**i
)) {
2177 prop_list
= new PropertyList();
2179 prop_list
->add (rlp
);
2190 Playlist::set_state (const XMLNode
& node
, int version
)
2194 XMLNodeConstIterator niter
;
2195 XMLPropertyList plist
;
2196 XMLPropertyConstIterator piter
;
2198 boost::shared_ptr
<Region
> region
;
2203 if (node
.name() != "Playlist") {
2210 plist
= node
.properties();
2212 for (piter
= plist
.begin(); piter
!= plist
.end(); ++piter
) {
2216 if (prop
->name() == X_("name")) {
2217 _name
= prop
->value();
2219 } else if (prop
->name() == X_("id")) {
2220 _id
= prop
->value();
2221 } else if (prop
->name() == X_("orig_diskstream_id")) {
2222 _orig_diskstream_id
= prop
->value ();
2223 } else if (prop
->name() == X_("frozen")) {
2224 _frozen
= string_is_affirmative (prop
->value());
2230 nlist
= node
.children();
2232 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
2236 if (child
->name() == "Region") {
2238 if ((prop
= child
->property ("id")) == 0) {
2239 error
<< _("region state node has no ID, ignored") << endmsg
;
2243 ID id
= prop
->value ();
2245 if ((region
= region_by_id (id
))) {
2247 region
->suspend_property_changes ();
2249 if (region
->set_state (*child
, version
)) {
2250 region
->resume_property_changes ();
2254 } else if ((region
= RegionFactory::create (_session
, *child
, true)) != 0) {
2255 region
->suspend_property_changes ();
2257 error
<< _("Playlist: cannot create region from XML") << endmsg
;
2261 add_region (region
, region
->position(), 1.0);
2263 // So that layer_op ordering doesn't get screwed up
2264 region
->set_last_layer_op( region
->layer());
2265 region
->resume_property_changes ();
2269 /* update dependents, which was not done during add_region_internal
2270 due to in_set_state being true
2273 for (RegionList::iterator r
= regions
.begin(); r
!= regions
.end(); ++r
) {
2274 check_dependents (*r
, false);
2278 notify_contents_changed ();
2281 first_set_state
= false;
2286 Playlist::get_state()
2288 return state (true);
2292 Playlist::get_template()
2294 return state (false);
2297 /** @param full_state true to include regions in the returned state, otherwise false.
2300 Playlist::state (bool full_state
)
2302 XMLNode
*node
= new XMLNode (X_("Playlist"));
2305 node
->add_property (X_("id"), id().to_s());
2306 node
->add_property (X_("name"), _name
);
2307 node
->add_property (X_("type"), _type
.to_string());
2309 _orig_diskstream_id
.print (buf
, sizeof (buf
));
2310 node
->add_property (X_("orig_diskstream_id"), buf
);
2311 node
->add_property (X_("frozen"), _frozen
? "yes" : "no");
2314 RegionLock
rlock (this, false);
2315 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2316 node
->add_child_nocopy ((*i
)->get_state());
2321 node
->add_child_copy (*_extra_xml
);
2328 Playlist::empty() const
2330 RegionLock
rlock (const_cast<Playlist
*>(this), false);
2331 return regions
.empty();
2335 Playlist::n_regions() const
2337 RegionLock
rlock (const_cast<Playlist
*>(this), false);
2338 return regions
.size();
2341 pair
<framecnt_t
, framecnt_t
>
2342 Playlist::get_extent () const
2344 RegionLock
rlock (const_cast<Playlist
*>(this), false);
2345 return _get_extent ();
2348 pair
<framecnt_t
, framecnt_t
>
2349 Playlist::_get_extent () const
2351 pair
<framecnt_t
, framecnt_t
> ext (max_frames
, 0);
2353 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2354 pair
<framecnt_t
, framecnt_t
> const e ((*i
)->position(), (*i
)->position() + (*i
)->length());
2355 if (e
.first
< ext
.first
) {
2356 ext
.first
= e
.first
;
2358 if (e
.second
> ext
.second
) {
2359 ext
.second
= e
.second
;
2367 Playlist::bump_name (string name
, Session
&session
)
2369 string newname
= name
;
2372 newname
= bump_name_once (newname
, '.');
2373 } while (session
.playlists
->by_name (newname
)!=NULL
);
2380 Playlist::top_layer() const
2382 RegionLock
rlock (const_cast<Playlist
*> (this));
2385 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2386 top
= max (top
, (*i
)->layer());
2392 Playlist::set_edit_mode (EditMode mode
)
2397 /********************
2399 ********************/
2402 Playlist::relayer ()
2404 /* never compute layers when changing state for undo/redo or setting from XML */
2406 if (in_update
|| in_set_state
) {
2410 bool changed
= false;
2412 /* Build up a new list of regions on each layer, stored in a set of lists
2413 each of which represent some period of time on some layer. The idea
2414 is to avoid having to search the entire region list to establish whether
2415 each region overlaps another */
2417 /* how many pieces to divide this playlist's time up into */
2418 int const divisions
= 512;
2420 /* find the start and end positions of the regions on this playlist */
2421 framepos_t start
= INT64_MAX
;
2423 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2424 start
= min (start
, (*i
)->position());
2425 end
= max (end
, (*i
)->position() + (*i
)->length());
2428 /* hence the size of each time division */
2429 double const division_size
= (end
- start
) / double (divisions
);
2431 vector
<vector
<RegionList
> > layers
;
2432 layers
.push_back (vector
<RegionList
> (divisions
));
2434 /* we want to go through regions from desired lowest to desired highest layer,
2435 which depends on the layer model
2438 RegionList copy
= regions
.rlist();
2440 /* sort according to the model and the layering mode that we're in */
2442 if (_explicit_relayering
) {
2444 copy
.sort (RegionSortByLayerWithPending ());
2446 } else if (_session
.config
.get_layer_model() == MoveAddHigher
|| _session
.config
.get_layer_model() == AddHigher
) {
2448 copy
.sort (RegionSortByLastLayerOp ());
2453 for (RegionList::iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
2455 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2456 (*i
)->set_pending_explicit_relayer (false);
2458 /* find the time divisions that this region covers; if there are no regions on the list,
2459 division_size will equal 0 and in this case we'll just say that
2460 start_division = end_division = 0.
2462 int start_division
= 0;
2463 int end_division
= 0;
2465 if (division_size
> 0) {
2466 start_division
= floor ( ((*i
)->position() - start
) / division_size
);
2467 end_division
= floor ( ((*i
)->position() + (*i
)->length() - start
) / division_size
);
2468 if (end_division
== divisions
) {
2473 assert (divisions
== 0 || end_division
< divisions
);
2475 /* find the lowest layer that this region can go on */
2476 size_t j
= layers
.size();
2478 /* try layer j - 1; it can go on if it overlaps no other region
2479 that is already on that layer
2482 bool overlap
= false;
2483 for (int k
= start_division
; k
<= end_division
; ++k
) {
2484 RegionList::iterator l
= layers
[j
-1][k
].begin ();
2485 while (l
!= layers
[j
-1][k
].end()) {
2486 if ((*l
)->overlap_equivalent (*i
)) {
2499 /* overlap, so we must use layer j */
2506 if (j
== layers
.size()) {
2507 /* we need a new layer for this region */
2508 layers
.push_back (vector
<RegionList
> (divisions
));
2511 /* put a reference to this region in each of the divisions that it exists in */
2512 for (int k
= start_division
; k
<= end_division
; ++k
) {
2513 layers
[j
][k
].push_back (*i
);
2516 if ((*i
)->layer() != j
) {
2520 (*i
)->set_layer (j
);
2524 notify_layering_changed ();
2528 /* XXX these layer functions are all deprecated */
2531 Playlist::raise_region (boost::shared_ptr
<Region
> region
)
2533 uint32_t rsz
= regions
.size();
2534 layer_t target
= region
->layer() + 1U;
2536 if (target
>= rsz
) {
2537 /* its already at the effective top */
2541 move_region_to_layer (target
, region
, 1);
2545 Playlist::lower_region (boost::shared_ptr
<Region
> region
)
2547 if (region
->layer() == 0) {
2548 /* its already at the bottom */
2552 layer_t target
= region
->layer() - 1U;
2554 move_region_to_layer (target
, region
, -1);
2558 Playlist::raise_region_to_top (boost::shared_ptr
<Region
> region
)
2560 /* does nothing useful if layering mode is later=higher */
2561 switch (_session
.config
.get_layer_model()) {
2568 layer_t top
= regions
.size() - 1;
2570 if (region
->layer() >= top
) {
2571 /* already on the top */
2575 move_region_to_layer (top
, region
, 1);
2576 /* mark the region's last_layer_op as now, so that it remains on top when
2577 doing future relayers (until something else takes over)
2579 timestamp_layer_op (region
);
2583 Playlist::lower_region_to_bottom (boost::shared_ptr
<Region
> region
)
2585 /* does nothing useful if layering mode is later=higher */
2586 switch (_session
.config
.get_layer_model()) {
2593 if (region
->layer() == 0) {
2594 /* already on the bottom */
2598 move_region_to_layer (0, region
, -1);
2599 /* force region's last layer op to zero so that it stays at the bottom
2600 when doing future relayers
2602 region
->set_last_layer_op (0);
2606 Playlist::move_region_to_layer (layer_t target_layer
, boost::shared_ptr
<Region
> region
, int dir
)
2608 RegionList::iterator i
;
2609 typedef pair
<boost::shared_ptr
<Region
>,layer_t
> LayerInfo
;
2610 list
<LayerInfo
> layerinfo
;
2613 RegionLock
rlock (const_cast<Playlist
*> (this));
2615 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2625 /* region is moving up, move all regions on intermediate layers
2629 if ((*i
)->layer() > region
->layer() && (*i
)->layer() <= target_layer
) {
2630 dest
= (*i
)->layer() - 1;
2637 /* region is moving down, move all regions on intermediate layers
2641 if ((*i
)->layer() < region
->layer() && (*i
)->layer() >= target_layer
) {
2642 dest
= (*i
)->layer() + 1;
2652 newpair
.second
= dest
;
2654 layerinfo
.push_back (newpair
);
2658 /* now reset the layers without holding the region lock */
2660 for (list
<LayerInfo
>::iterator x
= layerinfo
.begin(); x
!= layerinfo
.end(); ++x
) {
2661 x
->first
->set_layer (x
->second
);
2664 region
->set_layer (target_layer
);
2667 /* now check all dependents */
2669 for (list
<LayerInfo
>::iterator x
= layerinfo
.begin(); x
!= layerinfo
.end(); ++x
) {
2670 check_dependents (x
->first
, false);
2673 check_dependents (region
, false);
2680 Playlist::nudge_after (framepos_t start
, framecnt_t distance
, bool forwards
)
2682 RegionList::iterator i
;
2688 RegionLock
rlock (const_cast<Playlist
*> (this));
2690 for (i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2692 if ((*i
)->position() >= start
) {
2698 if ((*i
)->last_frame() > max_frames
- distance
) {
2699 new_pos
= max_frames
- (*i
)->length();
2701 new_pos
= (*i
)->position() + distance
;
2706 if ((*i
)->position() > distance
) {
2707 new_pos
= (*i
)->position() - distance
;
2713 (*i
)->set_position (new_pos
, this);
2721 notify_length_changed ();
2726 boost::shared_ptr
<Region
>
2727 Playlist::find_region (const ID
& id
) const
2729 RegionLock
rlock (const_cast<Playlist
*> (this));
2731 /* searches all regions currently in use by the playlist */
2733 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2734 if ((*i
)->id() == id
) {
2739 return boost::shared_ptr
<Region
> ();
2742 boost::shared_ptr
<Region
>
2743 Playlist::region_by_id (const ID
& id
)
2745 /* searches all regions ever added to this playlist */
2747 for (set
<boost::shared_ptr
<Region
> >::iterator i
= all_regions
.begin(); i
!= all_regions
.end(); ++i
) {
2748 if ((*i
)->id() == id
) {
2752 return boost::shared_ptr
<Region
> ();
2756 Playlist::dump () const
2758 boost::shared_ptr
<Region
> r
;
2760 cerr
<< "Playlist \"" << _name
<< "\" " << endl
2761 << regions
.size() << " regions "
2764 for (RegionList::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2766 cerr
<< " " << r
->name() << " ["
2767 << r
->start() << "+" << r
->length()
2777 Playlist::set_frozen (bool yn
)
2783 Playlist::timestamp_layer_op (boost::shared_ptr
<Region
> region
)
2785 region
->set_last_layer_op (++layer_op_counter
);
2790 Playlist::shuffle (boost::shared_ptr
<Region
> region
, int dir
)
2794 if (region
->locked()) {
2801 RegionLock
rlock (const_cast<Playlist
*> (this));
2806 RegionList::iterator next
;
2808 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2809 if ((*i
) == region
) {
2813 if (next
!= regions
.end()) {
2815 if ((*next
)->locked()) {
2821 if ((*next
)->position() != region
->last_frame() + 1) {
2822 /* they didn't used to touch, so after shuffle,
2823 just have them swap positions.
2825 new_pos
= (*next
)->position();
2827 /* they used to touch, so after shuffle,
2828 make sure they still do. put the earlier
2829 region where the later one will end after
2832 new_pos
= region
->position() + (*next
)->length();
2835 (*next
)->set_position (region
->position(), this);
2836 region
->set_position (new_pos
, this);
2838 /* avoid a full sort */
2840 regions
.erase (i
); // removes the region from the list */
2842 regions
.insert (next
, region
); // adds it back after next
2851 RegionList::iterator prev
= regions
.end();
2853 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); prev
= i
, ++i
) {
2854 if ((*i
) == region
) {
2856 if (prev
!= regions
.end()) {
2858 if ((*prev
)->locked()) {
2863 if (region
->position() != (*prev
)->last_frame() + 1) {
2864 /* they didn't used to touch, so after shuffle,
2865 just have them swap positions.
2867 new_pos
= region
->position();
2869 /* they used to touch, so after shuffle,
2870 make sure they still do. put the earlier
2871 one where the later one will end after
2873 new_pos
= (*prev
)->position() + region
->length();
2876 region
->set_position ((*prev
)->position(), this);
2877 (*prev
)->set_position (new_pos
, this);
2879 /* avoid a full sort */
2881 regions
.erase (i
); // remove region
2882 regions
.insert (prev
, region
); // insert region before prev
2898 check_dependents (region
, false);
2900 notify_contents_changed();
2906 Playlist::region_is_shuffle_constrained (boost::shared_ptr
<Region
>)
2908 RegionLock
rlock (const_cast<Playlist
*> (this));
2910 if (regions
.size() > 1) {
2918 Playlist::update_after_tempo_map_change ()
2920 RegionLock
rlock (const_cast<Playlist
*> (this));
2921 RegionList
copy (regions
.rlist());
2925 for (RegionList::iterator i
= copy
.begin(); i
!= copy
.end(); ++i
) {
2926 (*i
)->update_position_after_tempo_map_change ();
2933 Playlist::foreach_region (boost::function
<void(boost::shared_ptr
<Region
>)> s
)
2935 RegionLock
rl (this, false);
2936 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2942 Playlist::set_explicit_relayering (bool e
)
2944 if (e
== false && _explicit_relayering
== true) {
2946 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2947 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2948 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2949 at this point would keep regions on the same layers.
2951 From then on in, it's just you and your towel.
2954 RegionLock
rl (this);
2955 for (RegionList::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
2956 (*i
)->set_last_layer_op ((*i
)->layer ());
2960 _explicit_relayering
= e
;
2965 Playlist::has_region_at (framepos_t
const p
) const
2967 RegionLock (const_cast<Playlist
*> (this));
2969 RegionList::const_iterator i
= regions
.begin ();
2970 while (i
!= regions
.end() && !(*i
)->covers (p
)) {
2974 return (i
!= regions
.end());
2977 /** Remove any region that uses a given source */
2979 Playlist::remove_region_by_source (boost::shared_ptr
<Source
> s
)
2981 RegionLock
rl (this);
2983 RegionList::iterator i
= regions
.begin();
2984 while (i
!= regions
.end()) {
2985 RegionList::iterator j
= i
;
2988 if ((*i
)->uses_source (s
)) {
2989 remove_region_internal (*i
);