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.
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "pbd/stacktrace.h"
30 #include "pbd/enumwriter.h"
32 #include "ardour/debug.h"
33 #include "ardour/region.h"
34 #include "ardour/playlist.h"
35 #include "ardour/session.h"
36 #include "ardour/source.h"
37 #include "ardour/tempo.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/filter.h"
40 #include "ardour/profile.h"
41 #include "ardour/utils.h"
46 using namespace ARDOUR
;
50 namespace Properties
{
51 PBD::PropertyDescriptor
<bool> muted
;
52 PBD::PropertyDescriptor
<bool> opaque
;
53 PBD::PropertyDescriptor
<bool> locked
;
54 PBD::PropertyDescriptor
<bool> automatic
;
55 PBD::PropertyDescriptor
<bool> whole_file
;
56 PBD::PropertyDescriptor
<bool> import
;
57 PBD::PropertyDescriptor
<bool> external
;
58 PBD::PropertyDescriptor
<bool> sync_marked
;
59 PBD::PropertyDescriptor
<bool> left_of_split
;
60 PBD::PropertyDescriptor
<bool> right_of_split
;
61 PBD::PropertyDescriptor
<bool> hidden
;
62 PBD::PropertyDescriptor
<bool> position_locked
;
63 PBD::PropertyDescriptor
<framepos_t
> start
;
64 PBD::PropertyDescriptor
<framecnt_t
> length
;
65 PBD::PropertyDescriptor
<framepos_t
> position
;
66 PBD::PropertyDescriptor
<framecnt_t
> sync_position
;
67 PBD::PropertyDescriptor
<layer_t
> layer
;
68 PBD::PropertyDescriptor
<framepos_t
> ancestral_start
;
69 PBD::PropertyDescriptor
<framecnt_t
> ancestral_length
;
70 PBD::PropertyDescriptor
<float> stretch
;
71 PBD::PropertyDescriptor
<float> shift
;
75 PBD::Signal2
<void,boost::shared_ptr
<ARDOUR::Region
>,const PropertyChange
&> Region::RegionPropertyChanged
;
78 Region::make_property_quarks ()
80 Properties::muted
.property_id
= g_quark_from_static_string (X_("muted"));
81 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for muted = %1\n", Properties::muted
.property_id
));
82 Properties::opaque
.property_id
= g_quark_from_static_string (X_("opaque"));
83 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for opaque = %1\n", Properties::opaque
.property_id
));
84 Properties::locked
.property_id
= g_quark_from_static_string (X_("locked"));
85 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for locked = %1\n", Properties::locked
.property_id
));
86 Properties::automatic
.property_id
= g_quark_from_static_string (X_("automatic"));
87 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for automatic = %1\n", Properties::automatic
.property_id
));
88 Properties::whole_file
.property_id
= g_quark_from_static_string (X_("whole-file"));
89 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for whole-file = %1\n", Properties::whole_file
.property_id
));
90 Properties::import
.property_id
= g_quark_from_static_string (X_("import"));
91 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for import = %1\n", Properties::import
.property_id
));
92 Properties::external
.property_id
= g_quark_from_static_string (X_("external"));
93 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for external = %1\n", Properties::external
.property_id
));
94 Properties::sync_marked
.property_id
= g_quark_from_static_string (X_("sync-marked"));
95 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked
.property_id
));
96 Properties::left_of_split
.property_id
= g_quark_from_static_string (X_("left-of-split"));
97 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split
.property_id
));
98 Properties::right_of_split
.property_id
= g_quark_from_static_string (X_("right-of-split"));
99 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split
.property_id
));
100 Properties::hidden
.property_id
= g_quark_from_static_string (X_("hidden"));
101 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for hidden = %1\n", Properties::hidden
.property_id
));
102 Properties::position_locked
.property_id
= g_quark_from_static_string (X_("position-locked"));
103 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for position-locked = %1\n", Properties::position_locked
.property_id
));
104 Properties::start
.property_id
= g_quark_from_static_string (X_("start"));
105 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for start = %1\n", Properties::start
.property_id
));
106 Properties::length
.property_id
= g_quark_from_static_string (X_("length"));
107 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for length = %1\n", Properties::length
.property_id
));
108 Properties::position
.property_id
= g_quark_from_static_string (X_("position"));
109 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for position = %1\n", Properties::position
.property_id
));
110 Properties::sync_position
.property_id
= g_quark_from_static_string (X_("sync-position"));
111 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for sync-position = %1\n", Properties::sync_position
.property_id
));
112 Properties::layer
.property_id
= g_quark_from_static_string (X_("layer"));
113 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for layer = %1\n", Properties::layer
.property_id
));
114 Properties::ancestral_start
.property_id
= g_quark_from_static_string (X_("ancestral-start"));
115 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start
.property_id
));
116 Properties::ancestral_length
.property_id
= g_quark_from_static_string (X_("ancestral-length"));
117 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length
.property_id
));
118 Properties::stretch
.property_id
= g_quark_from_static_string (X_("stretch"));
119 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for stretch = %1\n", Properties::stretch
.property_id
));
120 Properties::shift
.property_id
= g_quark_from_static_string (X_("shift"));
121 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for shift = %1\n", Properties::shift
.property_id
));
125 Region::register_properties ()
127 _xml_node_name
= X_("Region");
129 add_property (_muted
);
130 add_property (_opaque
);
131 add_property (_locked
);
132 add_property (_automatic
);
133 add_property (_whole_file
);
134 add_property (_import
);
135 add_property (_external
);
136 add_property (_sync_marked
);
137 add_property (_left_of_split
);
138 add_property (_right_of_split
);
139 add_property (_hidden
);
140 add_property (_position_locked
);
141 add_property (_start
);
142 add_property (_length
);
143 add_property (_position
);
144 add_property (_sync_position
);
145 add_property (_layer
);
146 add_property (_ancestral_start
);
147 add_property (_ancestral_length
);
148 add_property (_stretch
);
149 add_property (_shift
);
152 #define REGION_DEFAULT_STATE(s,l) \
153 _muted (Properties::muted, false) \
154 , _opaque (Properties::opaque, true) \
155 , _locked (Properties::locked, false) \
156 , _automatic (Properties::automatic, false) \
157 , _whole_file (Properties::whole_file, false) \
158 , _import (Properties::import, false) \
159 , _external (Properties::external, false) \
160 , _sync_marked (Properties::sync_marked, false) \
161 , _left_of_split (Properties::left_of_split, false) \
162 , _right_of_split (Properties::right_of_split, false) \
163 , _hidden (Properties::hidden, false) \
164 , _position_locked (Properties::position_locked, false) \
165 , _start (Properties::start, (s)) \
166 , _length (Properties::length, (l)) \
167 , _position (Properties::position, 0) \
168 , _sync_position (Properties::sync_position, (s)) \
169 , _layer (Properties::layer, 0) \
170 , _ancestral_start (Properties::ancestral_start, (s)) \
171 , _ancestral_length (Properties::ancestral_length, (l)) \
172 , _stretch (Properties::stretch, 1.0) \
173 , _shift (Properties::shift, 1.0)
175 #define REGION_COPY_STATE(other) \
176 _muted (other->_muted) \
177 , _opaque (other->_opaque) \
178 , _locked (other->_locked) \
179 , _automatic (other->_automatic) \
180 , _whole_file (other->_whole_file) \
181 , _import (other->_import) \
182 , _external (other->_external) \
183 , _sync_marked (other->_sync_marked) \
184 , _left_of_split (other->_left_of_split) \
185 , _right_of_split (other->_right_of_split) \
186 , _hidden (other->_hidden) \
187 , _position_locked (other->_position_locked) \
188 , _start(other->_start) \
189 , _length(other->_length) \
190 , _position(other->_position) \
191 , _sync_position(other->_sync_position) \
192 , _layer (other->_layer) \
193 , _ancestral_start (other->_ancestral_start) \
194 , _ancestral_length (other->_ancestral_length) \
195 , _stretch (other->_stretch) \
196 , _shift (other->_shift)
198 /* derived-from-derived constructor (no sources in constructor) */
199 Region::Region (Session
& s
, framepos_t start
, framecnt_t length
, const string
& name
, DataType type
)
200 : SessionObject(s
, name
)
202 , REGION_DEFAULT_STATE(start
,length
)
203 , _last_length (length
)
205 , _positional_lock_style(AudioTime
)
206 , _first_edit (EditChangesNothing
)
207 , _read_data_count(0)
209 , _pending_explicit_relayer (false)
211 register_properties ();
213 /* no sources at this point */
216 /** Basic Region constructor (many sources) */
217 Region::Region (const SourceList
& srcs
)
218 : SessionObject(srcs
.front()->session(), "toBeRenamed")
219 , _type (srcs
.front()->type())
220 , REGION_DEFAULT_STATE(0,0)
223 , _positional_lock_style (_type
== DataType::AUDIO
? AudioTime
: MusicTime
)
224 , _first_edit (EditChangesNothing
)
225 , _valid_transients(false)
226 , _read_data_count(0)
228 , _pending_explicit_relayer (false)
230 register_properties ();
232 _type
= srcs
.front()->type();
236 assert(_sources
.size() > 0);
237 assert (_type
== srcs
.front()->type());
240 /** Create a new Region from part of an existing one, starting at one of two places:
242 if @param offset_relative is true, then the start within @param other is given by @param offset
243 (i.e. relative to the start of @param other's sources, the start is @param offset + @param other.start()
245 if @param offset_relative is false, then the start within the source is given @param offset.
247 Region::Region (boost::shared_ptr
<const Region
> other
, frameoffset_t offset
, bool offset_relative
)
248 : SessionObject(other
->session(), other
->name())
249 , _type (other
->data_type())
250 , REGION_COPY_STATE (other
)
251 , _last_length (other
->_last_length
)
252 , _last_position(other
->_last_position
) \
253 , _positional_lock_style(other
->_positional_lock_style
) \
254 , _first_edit (EditChangesNothing
)
255 , _valid_transients(false)
256 , _read_data_count(0)
258 , _pending_explicit_relayer (false)
261 register_properties ();
263 /* override state that may have been incorrectly inherited from the other region
271 use_sources (other
->_sources
);
273 if (!offset_relative
) {
275 /* not sure why we do this, but its a hangover from ardour before
276 property lists. this would be nice to remove.
279 _positional_lock_style
= other
->_positional_lock_style
;
280 _first_edit
= other
->_first_edit
;
286 /* sync pos is relative to start of file. our start-in-file is now zero,
287 so set our sync position to whatever the the difference between
288 _start and _sync_pos was in the other region.
290 result is that our new sync pos points to the same point in our source(s)
291 as the sync in the other region did in its source(s).
293 since we start at zero in our source(s), it is not possible to use a sync point that
294 is before the start. reset it to _start if that was true in the other region.
297 if (other
->sync_marked()) {
298 if (other
->_start
< other
->_sync_position
) {
299 /* sync pos was after the start point of the other region */
300 _sync_position
= other
->_sync_position
- other
->_start
;
302 /* sync pos was before the start point of the other region. not possible here. */
303 _sync_marked
= false;
304 _sync_position
= _start
;
307 _sync_marked
= false;
308 _sync_position
= _start
;
311 /* XXX do something else ! */
312 fatal
<< string_compose (_("programming error: %1"), X_("Region+offset constructor used with illegal combination of offset+relative"))
319 _start
= other
->_start
+ offset
;
321 /* if the other region had a distinct sync point
322 set, then continue to use it as best we can.
323 otherwise, reset sync point back to start.
326 if (other
->sync_marked()) {
327 if (other
->_sync_position
< _start
) {
328 _sync_marked
= false;
329 _sync_position
= _start
;
331 _sync_position
= other
->_sync_position
;
334 _sync_marked
= false;
335 _sync_position
= _start
;
339 if (Profile
->get_sae()) {
340 /* reset sync point to start if its ended up
341 outside region bounds.
344 if (_sync_position
< _start
|| _sync_position
>= _start
+ _length
) {
345 _sync_marked
= false;
346 _sync_position
= _start
;
350 assert (_type
== other
->data_type());
353 /** Create a copy of @param other but with different sources. Used by filters */
354 Region::Region (boost::shared_ptr
<const Region
> other
, const SourceList
& srcs
)
355 : SessionObject (other
->session(), other
->name())
356 , _type (srcs
.front()->type())
357 , REGION_COPY_STATE (other
)
358 , _last_length (other
->_last_length
)
359 , _last_position (other
->_last_position
)
360 , _positional_lock_style (other
->_positional_lock_style
)
361 , _first_edit (EditChangesID
)
362 , _valid_transients (false)
363 , _read_data_count (0)
364 , _last_layer_op (other
->_last_layer_op
)
365 , _pending_explicit_relayer (false)
367 register_properties ();
370 _position_locked
= false;
372 other
->_first_edit
= EditChangesName
;
374 if (other
->_extra_xml
) {
375 _extra_xml
= new XMLNode (*other
->_extra_xml
);
381 assert(_sources
.size() > 0);
384 /** Simple "copy" constructor */
385 Region::Region (boost::shared_ptr
<const Region
> other
)
386 : SessionObject(other
->session(), other
->name())
387 , _type(other
->data_type())
388 , REGION_COPY_STATE (other
)
389 , _last_length (other
->_last_length
)
390 , _last_position (other
->_last_position
)
391 , _positional_lock_style (other
->_positional_lock_style
)
392 , _first_edit (EditChangesID
)
393 , _valid_transients(false)
394 , _read_data_count(0)
395 , _last_layer_op(other
->_last_layer_op
)
396 , _pending_explicit_relayer (false)
398 register_properties ();
401 _position_locked
= false;
403 other
->_first_edit
= EditChangesName
;
405 if (other
->_extra_xml
) {
406 _extra_xml
= new XMLNode (*other
->_extra_xml
);
411 use_sources (other
->_sources
);
412 assert(_sources
.size() > 0);
417 DEBUG_TRACE (DEBUG::Destruction
, string_compose ("Region %1 destructor @ %2\n", _name
, this));
422 Region::set_playlist (boost::weak_ptr
<Playlist
> wpl
)
424 _playlist
= wpl
.lock();
428 Region::set_name (const std::string
& str
)
431 SessionObject::set_name(str
); // EMIT SIGNAL NameChanged()
432 assert(_name
== str
);
433 send_change (Properties::name
);
440 Region::set_length (framecnt_t len
, void */
*src*/
)
442 //cerr << "Region::set_length() len = " << len << endl;
447 if (_length
!= len
&& len
!= 0) {
449 /* check that the current _position wouldn't make the new
453 if (max_frames
- len
< _position
) {
457 if (!verify_length (len
)) {
462 _last_length
= _length
;
467 invalidate_transients ();
469 if (!property_changes_suspended()) {
473 send_change (Properties::length
);
478 Region::maybe_uncopy ()
480 /* this does nothing but marked a semantic moment once upon a time */
484 Region::first_edit ()
486 boost::shared_ptr
<Playlist
> pl (playlist());
488 if (_first_edit
!= EditChangesNothing
&& pl
) {
490 _name
= RegionFactory::new_region_name (_name
);
491 _first_edit
= EditChangesNothing
;
493 send_change (Properties::name
);
494 RegionFactory::CheckNewRegion (shared_from_this());
499 Region::at_natural_position () const
501 boost::shared_ptr
<Playlist
> pl (playlist());
507 boost::shared_ptr
<Region
> whole_file_region
= get_parent();
509 if (whole_file_region
) {
510 if (_position
== whole_file_region
->position() + _start
) {
519 Region::move_to_natural_position (void *src
)
521 boost::shared_ptr
<Playlist
> pl (playlist());
527 boost::shared_ptr
<Region
> whole_file_region
= get_parent();
529 if (whole_file_region
) {
530 set_position (whole_file_region
->position() + _start
, src
);
535 Region::special_set_position (framepos_t pos
)
537 /* this is used when creating a whole file region as
538 a way to store its "natural" or "captured" position.
541 _position
= _position
;
546 Region::set_position_lock_style (PositionLockStyle ps
)
548 boost::shared_ptr
<Playlist
> pl (playlist());
554 _positional_lock_style
= ps
;
556 if (_positional_lock_style
== MusicTime
) {
557 _session
.tempo_map().bbt_time (_position
, _bbt_time
);
563 Region::update_position_after_tempo_map_change ()
565 boost::shared_ptr
<Playlist
> pl (playlist());
567 if (!pl
|| _positional_lock_style
!= MusicTime
) {
571 TempoMap
& map (_session
.tempo_map());
572 framepos_t pos
= map
.frame_time (_bbt_time
);
573 set_position_internal (pos
, false);
577 Region::set_position (framepos_t pos
, void* /*src*/)
583 set_position_internal (pos
, true);
587 Region::set_position_internal (framepos_t pos
, bool allow_bbt_recompute
)
589 if (_position
!= pos
) {
590 _last_position
= _position
;
593 /* check that the new _position wouldn't make the current
594 length impossible - if so, change the length.
596 XXX is this the right thing to do?
599 if (max_frames
- _length
< _position
) {
600 _last_length
= _length
;
601 _length
= max_frames
- _position
;
604 if (allow_bbt_recompute
) {
605 recompute_position_from_lock_style ();
608 invalidate_transients ();
611 /* do this even if the position is the same. this helps out
612 a GUI that has moved its representation already.
615 send_change (Properties::position
);
619 Region::set_position_on_top (framepos_t pos
, void* /*src*/)
625 if (_position
!= pos
) {
626 _last_position
= _position
;
630 boost::shared_ptr
<Playlist
> pl (playlist());
633 pl
->raise_region_to_top (shared_from_this ());
636 /* do this even if the position is the same. this helps out
637 a GUI that has moved its representation already.
640 send_change (Properties::position
);
644 Region::recompute_position_from_lock_style ()
646 if (_positional_lock_style
== MusicTime
) {
647 _session
.tempo_map().bbt_time (_position
, _bbt_time
);
652 Region::nudge_position (frameoffset_t n
, void* /*src*/)
662 _last_position
= _position
;
665 if (_position
> max_frames
- n
) {
666 _position
= max_frames
;
671 if (_position
< -n
) {
678 send_change (Properties::position
);
682 Region::set_ancestral_data (framepos_t s
, framecnt_t l
, float st
, float sh
)
684 _ancestral_length
= l
;
685 _ancestral_start
= s
;
691 Region::set_start (framepos_t pos
, void* /*src*/)
693 if (locked() || position_locked()) {
696 /* This just sets the start, nothing else. It effectively shifts
697 the contents of the Region within the overall extent of the Source,
698 without changing the Region's position or length
703 if (!verify_start (pos
)) {
710 invalidate_transients ();
712 send_change (Properties::start
);
717 Region::trim_start (framepos_t new_position
, void */
*src*/
)
719 if (locked() || position_locked()) {
722 framepos_t new_start
;
723 frameoffset_t start_shift
;
725 if (new_position
> _position
) {
726 start_shift
= new_position
- _position
;
728 start_shift
= -(_position
- new_position
);
731 if (start_shift
> 0) {
733 if (_start
> max_frames
- start_shift
) {
734 new_start
= max_frames
;
736 new_start
= _start
+ start_shift
;
739 if (!verify_start (new_start
)) {
743 } else if (start_shift
< 0) {
745 if (_start
< -start_shift
) {
748 new_start
= _start
+ start_shift
;
754 if (new_start
== _start
) {
762 send_change (Properties::start
);
766 Region::trim_front (framepos_t new_position
, void *src
)
768 modify_front (new_position
, false, src
);
772 Region::cut_front (nframes_t new_position
, void *src
)
774 modify_front (new_position
, true, src
);
778 Region::cut_end (nframes_t new_endpoint
, void *src
)
780 modify_end (new_endpoint
, true, src
);
784 Region::modify_front (nframes_t new_position
, bool reset_fade
, void *src
)
790 nframes_t end
= last_frame();
791 nframes_t source_zero
;
793 if (_position
> _start
) {
794 source_zero
= _position
- _start
;
796 source_zero
= 0; // its actually negative, but this will work for us
799 if (new_position
< end
) { /* can't trim it zero or negative length */
803 /* can't trim it back passed where source position zero is located */
805 new_position
= max (new_position
, source_zero
);
807 if (new_position
> _position
) {
808 newlen
= _length
- (new_position
- _position
);
810 newlen
= _length
+ (_position
- new_position
);
813 trim_to_internal (new_position
, newlen
, src
);
815 _right_of_split
= true;
818 if (!property_changes_suspended()) {
819 recompute_at_start ();
825 Region::modify_end (nframes_t new_endpoint
, bool reset_fade
, void *src
)
831 if (new_endpoint
> _position
) {
832 trim_to_internal (_position
, new_endpoint
- _position
+1, this);
834 _left_of_split
= true;
836 if (!property_changes_suspended()) {
842 /** @param new_endpoint New region end point, such that, for example,
843 * a region at 0 of length 10 has an endpoint of 9.
847 Region::trim_end (framepos_t new_endpoint
, void* src
)
849 modify_end (new_endpoint
, false, src
);
853 Region::trim_to (framepos_t position
, framecnt_t length
, void *src
)
859 trim_to_internal (position
, length
, src
);
861 if (!property_changes_suspended()) {
862 recompute_at_start ();
868 Region::trim_to_internal (framepos_t position
, framecnt_t length
, void */
*src*/
)
870 frameoffset_t start_shift
;
871 framepos_t new_start
;
877 if (position
> _position
) {
878 start_shift
= position
- _position
;
880 start_shift
= -(_position
- position
);
883 if (start_shift
> 0) {
885 if (_start
> max_frames
- start_shift
) {
886 new_start
= max_frames
;
888 new_start
= _start
+ start_shift
;
892 } else if (start_shift
< 0) {
894 if (_start
< -start_shift
) {
897 new_start
= _start
+ start_shift
;
903 if (!verify_start_and_length (new_start
, length
)) {
907 PropertyChange what_changed
;
909 if (_start
!= new_start
) {
911 what_changed
.add (Properties::start
);
913 if (_length
!= length
) {
914 if (!property_changes_suspended()) {
915 _last_length
= _length
;
918 what_changed
.add (Properties::length
);
920 if (_position
!= position
) {
921 if (!property_changes_suspended()) {
922 _last_position
= _position
;
924 _position
= position
;
925 what_changed
.add (Properties::position
);
930 PropertyChange start_and_length
;
932 start_and_length
.add (Properties::start
);
933 start_and_length
.add (Properties::length
);
935 if (what_changed
.contains (start_and_length
)) {
939 if (!what_changed
.empty()) {
940 send_change (what_changed
);
945 Region::set_hidden (bool yn
)
947 if (hidden() != yn
) {
949 send_change (Properties::hidden
);
954 Region::set_whole_file (bool yn
)
957 /* no change signal */
961 Region::set_automatic (bool yn
)
964 /* no change signal */
968 Region::set_muted (bool yn
)
972 send_change (Properties::muted
);
977 Region::set_opaque (bool yn
)
979 if (opaque() != yn
) {
981 send_change (Properties::opaque
);
986 Region::set_locked (bool yn
)
988 if (locked() != yn
) {
990 send_change (Properties::locked
);
995 Region::set_position_locked (bool yn
)
997 if (position_locked() != yn
) {
998 _position_locked
= yn
;
999 send_change (Properties::locked
);
1004 Region::set_sync_position (framepos_t absolute_pos
)
1006 framepos_t
const file_pos
= _start
+ (absolute_pos
- _position
);
1008 if (file_pos
!= _sync_position
) {
1009 _sync_marked
= true;
1010 _sync_position
= file_pos
;
1011 if (!property_changes_suspended()) {
1014 send_change (Properties::sync_position
);
1019 Region::clear_sync_position ()
1021 if (sync_marked()) {
1022 _sync_marked
= false;
1023 if (!property_changes_suspended()) {
1026 send_change (Properties::sync_position
);
1031 Region::sync_offset (int& dir
) const
1033 /* returns the sync point relative the first frame of the region */
1035 if (sync_marked()) {
1036 if (_sync_position
> _start
) {
1038 return _sync_position
- _start
;
1041 return _start
- _sync_position
;
1050 Region::adjust_to_sync (framepos_t pos
) const
1053 frameoffset_t offset
= sync_offset (sync_dir
);
1055 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1064 if (max_frames
- pos
> offset
) {
1073 Region::sync_position() const
1075 if (sync_marked()) {
1076 return _sync_position
;
1085 boost::shared_ptr
<Playlist
> pl (playlist());
1087 pl
->raise_region (shared_from_this ());
1094 boost::shared_ptr
<Playlist
> pl (playlist());
1096 pl
->lower_region (shared_from_this ());
1102 Region::raise_to_top ()
1104 boost::shared_ptr
<Playlist
> pl (playlist());
1106 pl
->raise_region_to_top (shared_from_this());
1111 Region::lower_to_bottom ()
1113 boost::shared_ptr
<Playlist
> pl (playlist());
1115 pl
->lower_region_to_bottom (shared_from_this());
1120 Region::set_layer (layer_t l
)
1125 send_change (Properties::layer
);
1130 Region::state (bool full
)
1132 XMLNode
*node
= new XMLNode ("Region");
1135 LocaleGuard
lg (X_("POSIX"));
1136 const char* fe
= NULL
;
1138 add_properties (*node
);
1140 _id
.print (buf
, sizeof (buf
));
1141 node
->add_property ("id", buf
);
1142 node
->add_property ("type", _type
.to_string());
1144 switch (_first_edit
) {
1145 case EditChangesNothing
:
1148 case EditChangesName
:
1154 default: /* should be unreachable but makes g++ happy */
1159 node
->add_property ("first-edit", fe
);
1161 /* note: flags are stored by derived classes */
1163 if (_positional_lock_style
!= AudioTime
) {
1164 node
->add_property ("positional-lock-style", enum_2_string (_positional_lock_style
));
1167 node
->add_property ("bbt-position", str
.str());
1170 for (uint32_t n
=0; n
< _sources
.size(); ++n
) {
1171 snprintf (buf2
, sizeof(buf2
), "source-%d", n
);
1172 _sources
[n
]->id().print (buf
, sizeof(buf
));
1173 node
->add_property (buf2
, buf
);
1176 for (uint32_t n
=0; n
< _master_sources
.size(); ++n
) {
1177 snprintf (buf2
, sizeof(buf2
), "master-source-%d", n
);
1178 _master_sources
[n
]->id().print (buf
, sizeof (buf
));
1179 node
->add_property (buf2
, buf
);
1182 if (full
&& _extra_xml
) {
1183 node
->add_child_copy (*_extra_xml
);
1190 Region::get_state ()
1192 return state (true);
1196 Region::set_state (const XMLNode
& node
, int version
)
1198 PropertyChange what_changed
;
1199 return _set_state (node
, version
, what_changed
, true);
1203 Region::_set_state (const XMLNode
& node
, int version
, PropertyChange
& what_changed
, bool send
)
1205 const XMLProperty
* prop
;
1207 what_changed
= set_properties (node
);
1209 if ((prop
= node
.property (X_("id")))) {
1210 _id
= prop
->value();
1213 if ((prop
= node
.property ("positional-lock-style")) != 0) {
1214 _positional_lock_style
= PositionLockStyle (string_2_enum (prop
->value(), _positional_lock_style
));
1216 if (_positional_lock_style
== MusicTime
) {
1217 if ((prop
= node
.property ("bbt-position")) == 0) {
1218 /* missing BBT info, revert to audio time locking */
1219 _positional_lock_style
= AudioTime
;
1221 if (sscanf (prop
->value().c_str(), "%d|%d|%d",
1224 &_bbt_time
.ticks
) != 3) {
1225 _positional_lock_style
= AudioTime
;
1232 /* fix problems with old sessions corrupted by impossible
1233 values for _stretch or _shift
1235 if (_stretch
== 0.0f
) {
1239 if (_shift
== 0.0f
) {
1243 const XMLNodeList
& nlist
= node
.children();
1245 for (XMLNodeConstIterator niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
1251 if (child
->name () == "Extra") {
1253 _extra_xml
= new XMLNode (*child
);
1259 send_change (what_changed
);
1262 /* Quick fix for 2.x sessions when region is muted */
1263 if ((prop
= node
.property (X_("flags")))) {
1264 if (string::npos
!= prop
->value().find("Muted")){
1274 Region::suspend_property_changes ()
1276 Stateful::suspend_property_changes ();
1277 _last_length
= _length
;
1278 _last_position
= _position
;
1282 Region::mid_thaw (const PropertyChange
& what_changed
)
1284 if (what_changed
.contains (Properties::length
)) {
1285 if (what_changed
.contains (Properties::position
)) {
1286 recompute_at_start ();
1288 recompute_at_end ();
1293 Region::send_change (const PropertyChange
& what_changed
)
1295 if (what_changed
.empty()) {
1299 Stateful::send_change (what_changed
);
1301 if (!_no_property_changes
) {
1303 /* Try and send a shared_pointer unless this is part of the constructor.
1308 boost::shared_ptr
<Region
> rptr
= shared_from_this();
1309 RegionPropertyChanged (rptr
, what_changed
);
1311 /* no shared_ptr available, relax; */
1317 Region::set_last_layer_op (uint64_t when
)
1319 _last_layer_op
= when
;
1323 Region::overlap_equivalent (boost::shared_ptr
<const Region
> other
) const
1325 return coverage (other
->first_frame(), other
->last_frame()) != OverlapNone
;
1329 Region::equivalent (boost::shared_ptr
<const Region
> other
) const
1331 return _start
== other
->_start
&&
1332 _position
== other
->_position
&&
1333 _length
== other
->_length
;
1337 Region::size_equivalent (boost::shared_ptr
<const Region
> other
) const
1339 return _start
== other
->_start
&&
1340 _length
== other
->_length
;
1344 Region::region_list_equivalent (boost::shared_ptr
<const Region
> other
) const
1346 return size_equivalent (other
) && source_equivalent (other
) && _name
== other
->_name
;
1350 Region::source_deleted (boost::weak_ptr
<Source
>)
1354 if (!_session
.deletion_in_progress()) {
1355 /* this is a very special case: at least one of the region's
1356 sources has bee deleted, so invalidate all references to
1357 ourselves. Do NOT do this during session deletion, because
1358 then we run the risk that this will actually result
1359 in this object being deleted (as refcnt goes to zero)
1360 while emitting DropReferences.
1368 Region::master_source_names ()
1370 SourceList::iterator i
;
1372 vector
<string
> names
;
1373 for (i
= _master_sources
.begin(); i
!= _master_sources
.end(); ++i
) {
1374 names
.push_back((*i
)->name());
1381 Region::set_master_sources (const SourceList
& srcs
)
1383 for (SourceList::const_iterator i
= _master_sources
.begin (); i
!= _master_sources
.end(); ++i
) {
1384 cerr
<< name() << " " << id() << " DEC M SMS\n";
1385 (*i
)->dec_use_count ();
1388 _master_sources
= srcs
;
1389 assert (_sources
.size() == _master_sources
.size());
1391 for (SourceList::const_iterator i
= _master_sources
.begin (); i
!= _master_sources
.end(); ++i
) {
1392 (*i
)->inc_use_count ();
1397 Region::source_equivalent (boost::shared_ptr
<const Region
> other
) const
1402 SourceList::const_iterator i
;
1403 SourceList::const_iterator io
;
1405 for (i
= _sources
.begin(), io
= other
->_sources
.begin(); i
!= _sources
.end() && io
!= other
->_sources
.end(); ++i
, ++io
) {
1406 if ((*i
)->id() != (*io
)->id()) {
1411 for (i
= _master_sources
.begin(), io
= other
->_master_sources
.begin(); i
!= _master_sources
.end() && io
!= other
->_master_sources
.end(); ++i
, ++io
) {
1412 if ((*i
)->id() != (*io
)->id()) {
1421 Region::uses_source (boost::shared_ptr
<const Source
> source
) const
1423 for (SourceList::const_iterator i
= _sources
.begin(); i
!= _sources
.end(); ++i
) {
1432 Region::source_length(uint32_t n
) const
1434 assert (n
< _sources
.size());
1435 return _sources
[n
]->length(_position
- _start
);
1439 Region::verify_length (framecnt_t len
)
1441 if (source() && (source()->destructive() || source()->length_mutable())) {
1445 framecnt_t maxlen
= 0;
1447 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1448 maxlen
= max (maxlen
, source_length(n
) - _start
);
1451 len
= min (len
, maxlen
);
1457 Region::verify_start_and_length (framepos_t new_start
, framecnt_t
& new_length
)
1459 if (source() && (source()->destructive() || source()->length_mutable())) {
1463 framecnt_t maxlen
= 0;
1465 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1466 maxlen
= max (maxlen
, source_length(n
) - new_start
);
1469 new_length
= min (new_length
, maxlen
);
1475 Region::verify_start (framepos_t pos
)
1477 if (source() && (source()->destructive() || source()->length_mutable())) {
1481 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1482 if (pos
> source_length(n
) - _length
) {
1490 Region::verify_start_mutable (framepos_t
& new_start
)
1492 if (source() && (source()->destructive() || source()->length_mutable())) {
1496 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1497 if (new_start
> source_length(n
) - _length
) {
1498 new_start
= source_length(n
) - _length
;
1504 boost::shared_ptr
<Region
>
1505 Region::get_parent() const
1507 boost::shared_ptr
<Playlist
> pl (playlist());
1510 boost::shared_ptr
<Region
> r
;
1511 boost::shared_ptr
<Region
const> grrr2
= boost::dynamic_pointer_cast
<Region
const> (shared_from_this());
1513 if (grrr2
&& (r
= _session
.find_whole_file_parent (grrr2
))) {
1514 return boost::static_pointer_cast
<Region
> (r
);
1518 return boost::shared_ptr
<Region
>();
1522 Region::apply (Filter
& filter
)
1524 return filter
.run (shared_from_this());
1529 Region::invalidate_transients ()
1531 _valid_transients
= false;
1532 _transients
.clear ();
1536 Region::drop_sources ()
1538 for (SourceList::const_iterator i
= _sources
.begin (); i
!= _sources
.end(); ++i
) {
1539 cerr
<< name() << " " << id() << " DEC DS\n";
1540 (*i
)->dec_use_count ();
1545 for (SourceList::const_iterator i
= _master_sources
.begin (); i
!= _master_sources
.end(); ++i
) {
1546 cerr
<< name() << " " << id() << " DEC MDS \n";
1547 (*i
)->dec_use_count ();
1550 _master_sources
.clear ();
1554 Region::use_sources (SourceList
const & s
)
1556 set
<boost::shared_ptr
<Source
> > unique_srcs
;
1558 for (SourceList::const_iterator i
= s
.begin (); i
!= s
.end(); ++i
) {
1560 _sources
.push_back (*i
);
1561 (*i
)->inc_use_count ();
1562 _master_sources
.push_back (*i
);
1563 (*i
)->inc_use_count ();
1565 /* connect only once to DropReferences, even if sources are replicated
1568 if (unique_srcs
.find (*i
) == unique_srcs
.end ()) {
1569 unique_srcs
.insert (*i
);
1570 (*i
)->DropReferences
.connect_same_thread (*this, boost::bind (&Region::source_deleted
, this, boost::weak_ptr
<Source
>(*i
)));
1576 Region::property_factory (const XMLNode
& history_node
) const
1578 PropertyList
* prop_list
= new PropertyList
;
1580 for (OwnedPropertyList::const_iterator i
= _properties
->begin(); i
!= _properties
->end(); ++i
) {
1581 PropertyBase
* prop
= i
->second
->maybe_clone_self_if_found_in_history_node (history_node
);
1584 prop_list
->add (prop
);