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.
26 #include <glibmm/thread.h>
27 #include "pbd/xml++.h"
28 #include "pbd/stacktrace.h"
29 #include "pbd/enumwriter.h"
31 #include "ardour/debug.h"
32 #include "ardour/file_source.h"
33 #include "ardour/filter.h"
34 #include "ardour/playlist.h"
35 #include "ardour/profile.h"
36 #include "ardour/region.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/session.h"
39 #include "ardour/source.h"
40 #include "ardour/source_factory.h"
41 #include "ardour/tempo.h"
42 #include "ardour/utils.h"
47 using namespace ARDOUR
;
51 namespace Properties
{
52 PBD::PropertyDescriptor
<bool> muted
;
53 PBD::PropertyDescriptor
<bool> opaque
;
54 PBD::PropertyDescriptor
<bool> locked
;
55 PBD::PropertyDescriptor
<bool> automatic
;
56 PBD::PropertyDescriptor
<bool> whole_file
;
57 PBD::PropertyDescriptor
<bool> import
;
58 PBD::PropertyDescriptor
<bool> external
;
59 PBD::PropertyDescriptor
<bool> sync_marked
;
60 PBD::PropertyDescriptor
<bool> left_of_split
;
61 PBD::PropertyDescriptor
<bool> right_of_split
;
62 PBD::PropertyDescriptor
<bool> hidden
;
63 PBD::PropertyDescriptor
<bool> position_locked
;
64 PBD::PropertyDescriptor
<bool> valid_transients
;
65 PBD::PropertyDescriptor
<framepos_t
> start
;
66 PBD::PropertyDescriptor
<framecnt_t
> length
;
67 PBD::PropertyDescriptor
<framepos_t
> position
;
68 PBD::PropertyDescriptor
<framecnt_t
> sync_position
;
69 PBD::PropertyDescriptor
<layer_t
> layer
;
70 PBD::PropertyDescriptor
<framepos_t
> ancestral_start
;
71 PBD::PropertyDescriptor
<framecnt_t
> ancestral_length
;
72 PBD::PropertyDescriptor
<float> stretch
;
73 PBD::PropertyDescriptor
<float> shift
;
74 PBD::PropertyDescriptor
<PositionLockStyle
> position_lock_style
;
78 PBD::Signal2
<void,boost::shared_ptr
<ARDOUR::Region
>,const PropertyChange
&> Region::RegionPropertyChanged
;
81 Region::make_property_quarks ()
83 Properties::muted
.property_id
= g_quark_from_static_string (X_("muted"));
84 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for muted = %1\n", Properties::muted
.property_id
));
85 Properties::opaque
.property_id
= g_quark_from_static_string (X_("opaque"));
86 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for opaque = %1\n", Properties::opaque
.property_id
));
87 Properties::locked
.property_id
= g_quark_from_static_string (X_("locked"));
88 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for locked = %1\n", Properties::locked
.property_id
));
89 Properties::automatic
.property_id
= g_quark_from_static_string (X_("automatic"));
90 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for automatic = %1\n", Properties::automatic
.property_id
));
91 Properties::whole_file
.property_id
= g_quark_from_static_string (X_("whole-file"));
92 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for whole-file = %1\n", Properties::whole_file
.property_id
));
93 Properties::import
.property_id
= g_quark_from_static_string (X_("import"));
94 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for import = %1\n", Properties::import
.property_id
));
95 Properties::external
.property_id
= g_quark_from_static_string (X_("external"));
96 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for external = %1\n", Properties::external
.property_id
));
97 Properties::sync_marked
.property_id
= g_quark_from_static_string (X_("sync-marked"));
98 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked
.property_id
));
99 Properties::left_of_split
.property_id
= g_quark_from_static_string (X_("left-of-split"));
100 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split
.property_id
));
101 Properties::right_of_split
.property_id
= g_quark_from_static_string (X_("right-of-split"));
102 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split
.property_id
));
103 Properties::hidden
.property_id
= g_quark_from_static_string (X_("hidden"));
104 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for hidden = %1\n", Properties::hidden
.property_id
));
105 Properties::position_locked
.property_id
= g_quark_from_static_string (X_("position-locked"));
106 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for position-locked = %1\n", Properties::position_locked
.property_id
));
107 Properties::valid_transients
.property_id
= g_quark_from_static_string (X_("valid-transients"));
108 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients
.property_id
));
109 Properties::start
.property_id
= g_quark_from_static_string (X_("start"));
110 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for start = %1\n", Properties::start
.property_id
));
111 Properties::length
.property_id
= g_quark_from_static_string (X_("length"));
112 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for length = %1\n", Properties::length
.property_id
));
113 Properties::position
.property_id
= g_quark_from_static_string (X_("position"));
114 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for position = %1\n", Properties::position
.property_id
));
115 Properties::sync_position
.property_id
= g_quark_from_static_string (X_("sync-position"));
116 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for sync-position = %1\n", Properties::sync_position
.property_id
));
117 Properties::layer
.property_id
= g_quark_from_static_string (X_("layer"));
118 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for layer = %1\n", Properties::layer
.property_id
));
119 Properties::ancestral_start
.property_id
= g_quark_from_static_string (X_("ancestral-start"));
120 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start
.property_id
));
121 Properties::ancestral_length
.property_id
= g_quark_from_static_string (X_("ancestral-length"));
122 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length
.property_id
));
123 Properties::stretch
.property_id
= g_quark_from_static_string (X_("stretch"));
124 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for stretch = %1\n", Properties::stretch
.property_id
));
125 Properties::shift
.property_id
= g_quark_from_static_string (X_("shift"));
126 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for shift = %1\n", Properties::shift
.property_id
));
127 Properties::position_lock_style
.property_id
= g_quark_from_static_string (X_("positional-lock-style"));
128 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style
.property_id
));
132 Region::register_properties ()
134 _xml_node_name
= X_("Region");
136 add_property (_muted
);
137 add_property (_opaque
);
138 add_property (_locked
);
139 add_property (_automatic
);
140 add_property (_whole_file
);
141 add_property (_import
);
142 add_property (_external
);
143 add_property (_sync_marked
);
144 add_property (_left_of_split
);
145 add_property (_right_of_split
);
146 add_property (_hidden
);
147 add_property (_position_locked
);
148 add_property (_valid_transients
);
149 add_property (_start
);
150 add_property (_length
);
151 add_property (_position
);
152 add_property (_sync_position
);
153 add_property (_layer
);
154 add_property (_ancestral_start
);
155 add_property (_ancestral_length
);
156 add_property (_stretch
);
157 add_property (_shift
);
158 add_property (_position_lock_style
);
161 #define REGION_DEFAULT_STATE(s,l) \
162 _muted (Properties::muted, false) \
163 , _opaque (Properties::opaque, true) \
164 , _locked (Properties::locked, false) \
165 , _automatic (Properties::automatic, false) \
166 , _whole_file (Properties::whole_file, false) \
167 , _import (Properties::import, false) \
168 , _external (Properties::external, false) \
169 , _sync_marked (Properties::sync_marked, false) \
170 , _left_of_split (Properties::left_of_split, false) \
171 , _right_of_split (Properties::right_of_split, false) \
172 , _hidden (Properties::hidden, false) \
173 , _position_locked (Properties::position_locked, false) \
174 , _valid_transients (Properties::valid_transients, false) \
175 , _start (Properties::start, (s)) \
176 , _length (Properties::length, (l)) \
177 , _position (Properties::position, 0) \
178 , _sync_position (Properties::sync_position, (s)) \
179 , _layer (Properties::layer, 0) \
180 , _ancestral_start (Properties::ancestral_start, (s)) \
181 , _ancestral_length (Properties::ancestral_length, (l)) \
182 , _stretch (Properties::stretch, 1.0) \
183 , _shift (Properties::shift, 1.0) \
184 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
186 #define REGION_COPY_STATE(other) \
187 _muted (Properties::muted, other->_muted) \
188 , _opaque (Properties::opaque, other->_opaque) \
189 , _locked (Properties::locked, other->_locked) \
190 , _automatic (Properties::automatic, other->_automatic) \
191 , _whole_file (Properties::whole_file, other->_whole_file) \
192 , _import (Properties::import, other->_import) \
193 , _external (Properties::external, other->_external) \
194 , _sync_marked (Properties::sync_marked, other->_sync_marked) \
195 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
196 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
197 , _hidden (Properties::hidden, other->_hidden) \
198 , _position_locked (Properties::position_locked, other->_position_locked) \
199 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
200 , _start(Properties::start, other->_start) \
201 , _length(Properties::length, other->_length) \
202 , _position(Properties::position, other->_position) \
203 , _sync_position(Properties::sync_position, other->_sync_position) \
204 , _layer (Properties::layer, other->_layer) \
205 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
206 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
207 , _stretch (Properties::stretch, other->_stretch) \
208 , _shift (Properties::shift, other->_shift) \
209 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
211 /* derived-from-derived constructor (no sources in constructor) */
212 Region::Region (Session
& s
, framepos_t start
, framecnt_t length
, const string
& name
, DataType type
)
213 : SessionObject(s
, name
)
215 , REGION_DEFAULT_STATE(start
,length
)
216 , _last_length (length
)
218 , _first_edit (EditChangesNothing
)
219 , _read_data_count(0)
221 , _pending_explicit_relayer (false)
223 register_properties ();
225 /* no sources at this point */
228 /** Basic Region constructor (many sources) */
229 Region::Region (const SourceList
& srcs
)
230 : SessionObject(srcs
.front()->session(), "toBeRenamed")
231 , _type (srcs
.front()->type())
232 , REGION_DEFAULT_STATE(0,0)
235 , _first_edit (EditChangesNothing
)
236 , _read_data_count(0)
238 , _pending_explicit_relayer (false)
240 register_properties ();
242 _type
= srcs
.front()->type();
246 assert(_sources
.size() > 0);
247 assert (_type
== srcs
.front()->type());
250 /** Create a new Region from an existing one */
251 Region::Region (boost::shared_ptr
<const Region
> other
)
252 : SessionObject(other
->session(), other
->name())
253 , _type (other
->data_type())
254 , REGION_COPY_STATE (other
)
255 , _last_length (other
->_last_length
)
256 , _last_position(other
->_last_position
) \
257 , _first_edit (EditChangesNothing
)
258 , _read_data_count(0)
260 , _pending_explicit_relayer (false)
263 register_properties ();
265 /* override state that may have been incorrectly inherited from the other region
273 use_sources (other
->_sources
);
275 _position_lock_style
= other
->_position_lock_style
;
276 _first_edit
= other
->_first_edit
;
278 _start
= 0; // It seems strange _start is not inherited here?
280 /* sync pos is relative to start of file. our start-in-file is now zero,
281 so set our sync position to whatever the the difference between
282 _start and _sync_pos was in the other region.
284 result is that our new sync pos points to the same point in our source(s)
285 as the sync in the other region did in its source(s).
287 since we start at zero in our source(s), it is not possible to use a sync point that
288 is before the start. reset it to _start if that was true in the other region.
291 if (other
->sync_marked()) {
292 if (other
->_start
< other
->_sync_position
) {
293 /* sync pos was after the start point of the other region */
294 _sync_position
= other
->_sync_position
- other
->_start
;
296 /* sync pos was before the start point of the other region. not possible here. */
297 _sync_marked
= false;
298 _sync_position
= _start
;
301 _sync_marked
= false;
302 _sync_position
= _start
;
305 if (Profile
->get_sae()) {
306 /* reset sync point to start if its ended up
307 outside region bounds.
310 if (_sync_position
< _start
|| _sync_position
>= _start
+ _length
) {
311 _sync_marked
= false;
312 _sync_position
= _start
;
316 assert (_type
== other
->data_type());
319 /** Create a new Region from part of an existing one.
321 the start within \a other is given by \a offset
322 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
324 Region::Region (boost::shared_ptr
<const Region
> other
, frameoffset_t offset
)
325 : SessionObject(other
->session(), other
->name())
326 , _type (other
->data_type())
327 , REGION_COPY_STATE (other
)
328 , _last_length (other
->_last_length
)
329 , _last_position(other
->_last_position
) \
330 , _first_edit (EditChangesNothing
)
331 , _read_data_count(0)
333 , _pending_explicit_relayer (false)
336 register_properties ();
338 /* override state that may have been incorrectly inherited from the other region
346 use_sources (other
->_sources
);
348 _start
= other
->_start
+ offset
;
350 /* if the other region had a distinct sync point
351 set, then continue to use it as best we can.
352 otherwise, reset sync point back to start.
355 if (other
->sync_marked()) {
356 if (other
->_sync_position
< _start
) {
357 _sync_marked
= false;
358 _sync_position
= _start
;
360 _sync_position
= other
->_sync_position
;
363 _sync_marked
= false;
364 _sync_position
= _start
;
367 if (Profile
->get_sae()) {
368 /* reset sync point to start if its ended up
369 outside region bounds.
372 if (_sync_position
< _start
|| _sync_position
>= _start
+ _length
) {
373 _sync_marked
= false;
374 _sync_position
= _start
;
378 assert (_type
== other
->data_type());
381 /** Create a copy of @param other but with different sources. Used by filters */
382 Region::Region (boost::shared_ptr
<const Region
> other
, const SourceList
& srcs
)
383 : SessionObject (other
->session(), other
->name())
384 , _type (srcs
.front()->type())
385 , REGION_COPY_STATE (other
)
386 , _last_length (other
->_last_length
)
387 , _last_position (other
->_last_position
)
388 , _first_edit (EditChangesID
)
389 , _read_data_count (0)
390 , _last_layer_op (other
->_last_layer_op
)
391 , _pending_explicit_relayer (false)
393 register_properties ();
396 _position_locked
= false;
398 other
->_first_edit
= EditChangesName
;
400 if (other
->_extra_xml
) {
401 _extra_xml
= new XMLNode (*other
->_extra_xml
);
407 assert(_sources
.size() > 0);
412 DEBUG_TRACE (DEBUG::Destruction
, string_compose ("Region %1 destructor @ %2\n", _name
, this));
417 Region::set_playlist (boost::weak_ptr
<Playlist
> wpl
)
419 _playlist
= wpl
.lock();
423 Region::set_name (const std::string
& str
)
426 SessionObject::set_name(str
); // EMIT SIGNAL NameChanged()
427 assert(_name
== str
);
429 send_change (Properties::name
);
436 Region::set_length (framecnt_t len
)
438 //cerr << "Region::set_length() len = " << len << endl;
443 if (_length
!= len
&& len
!= 0) {
445 /* check that the current _position wouldn't make the new
449 if (max_framepos
- len
< _position
) {
453 if (!verify_length (len
)) {
458 _last_length
= _length
;
459 set_length_internal (len
);
463 invalidate_transients ();
465 if (!property_changes_suspended()) {
469 send_change (Properties::length
);
474 Region::set_length_internal (framecnt_t len
)
480 Region::maybe_uncopy ()
482 /* this does nothing but marked a semantic moment once upon a time */
486 Region::first_edit ()
488 boost::shared_ptr
<Playlist
> pl (playlist());
490 if (_first_edit
!= EditChangesNothing
&& pl
) {
492 _name
= RegionFactory::new_region_name (_name
);
493 _first_edit
= EditChangesNothing
;
495 send_change (Properties::name
);
497 RegionFactory::CheckNewRegion (shared_from_this());
502 Region::at_natural_position () const
504 boost::shared_ptr
<Playlist
> pl (playlist());
510 boost::shared_ptr
<Region
> whole_file_region
= get_parent();
512 if (whole_file_region
) {
513 if (_position
== whole_file_region
->position() + _start
) {
522 Region::move_to_natural_position ()
524 boost::shared_ptr
<Playlist
> pl (playlist());
530 boost::shared_ptr
<Region
> whole_file_region
= get_parent();
532 if (whole_file_region
) {
533 set_position (whole_file_region
->position() + _start
);
538 Region::special_set_position (framepos_t pos
)
540 /* this is used when creating a whole file region as
541 a way to store its "natural" or "captured" position.
544 _position
= _position
;
549 Region::set_position_lock_style (PositionLockStyle ps
)
551 if (_position_lock_style
!= ps
) {
553 boost::shared_ptr
<Playlist
> pl (playlist());
559 _position_lock_style
= ps
;
561 if (_position_lock_style
== MusicTime
) {
562 _session
.tempo_map().bbt_time (_position
, _bbt_time
);
565 send_change (Properties::position_lock_style
);
571 Region::update_position_after_tempo_map_change ()
573 boost::shared_ptr
<Playlist
> pl (playlist());
575 if (!pl
|| _position_lock_style
!= MusicTime
) {
579 TempoMap
& map (_session
.tempo_map());
580 framepos_t pos
= map
.frame_time (_bbt_time
);
581 set_position_internal (pos
, false);
583 /* do this even if the position is the same. this helps out
584 a GUI that has moved its representation already.
586 send_change (Properties::position
);
590 Region::set_position (framepos_t pos
)
596 set_position_internal (pos
, true);
598 /* do this even if the position is the same. this helps out
599 a GUI that has moved its representation already.
601 send_change (Properties::position
);
606 Region::set_position_internal (framepos_t pos
, bool allow_bbt_recompute
)
608 if (_position
!= pos
) {
609 _last_position
= _position
;
612 /* check that the new _position wouldn't make the current
613 length impossible - if so, change the length.
615 XXX is this the right thing to do?
618 if (max_framepos
- _length
< _position
) {
619 _last_length
= _length
;
620 _length
= max_framepos
- _position
;
623 if (allow_bbt_recompute
) {
624 recompute_position_from_lock_style ();
627 //invalidate_transients ();
632 Region::set_position_on_top (framepos_t pos
)
638 if (_position
!= pos
) {
639 set_position_internal (pos
, true);
642 boost::shared_ptr
<Playlist
> pl (playlist());
645 pl
->raise_region_to_top (shared_from_this ());
648 /* do this even if the position is the same. this helps out
649 a GUI that has moved its representation already.
651 send_change (Properties::position
);
655 Region::recompute_position_from_lock_style ()
657 if (_position_lock_style
== MusicTime
) {
658 _session
.tempo_map().bbt_time (_position
, _bbt_time
);
663 Region::nudge_position (frameoffset_t n
)
673 framepos_t new_position
= _position
;
676 if (_position
> max_framepos
- n
) {
677 new_position
= max_framepos
;
682 if (_position
< -n
) {
689 set_position_internal (new_position
, true);
691 send_change (Properties::position
);
695 Region::set_ancestral_data (framepos_t s
, framecnt_t l
, float st
, float sh
)
697 _ancestral_length
= l
;
698 _ancestral_start
= s
;
704 Region::set_start (framepos_t pos
)
706 if (locked() || position_locked()) {
709 /* This just sets the start, nothing else. It effectively shifts
710 the contents of the Region within the overall extent of the Source,
711 without changing the Region's position or length
716 if (!verify_start (pos
)) {
723 invalidate_transients ();
725 send_change (Properties::start
);
730 Region::trim_start (framepos_t new_position
)
732 if (locked() || position_locked()) {
735 framepos_t new_start
;
736 frameoffset_t
const start_shift
= new_position
- _position
;
738 if (start_shift
> 0) {
740 if (_start
> max_framepos
- start_shift
) {
741 new_start
= max_framepos
;
743 new_start
= _start
+ start_shift
;
746 if (!verify_start (new_start
)) {
750 } else if (start_shift
< 0) {
752 if (_start
< -start_shift
) {
755 new_start
= _start
+ start_shift
;
762 if (new_start
== _start
) {
770 send_change (Properties::start
);
774 Region::trim_front (framepos_t new_position
)
776 modify_front (new_position
, false);
780 Region::cut_front (framepos_t new_position
)
782 modify_front (new_position
, true);
786 Region::cut_end (framepos_t new_endpoint
)
788 modify_end (new_endpoint
, true);
792 Region::modify_front (framepos_t new_position
, bool reset_fade
)
798 framepos_t end
= last_frame();
799 framepos_t source_zero
;
801 if (_position
> _start
) {
802 source_zero
= _position
- _start
;
804 source_zero
= 0; // its actually negative, but this will work for us
807 if (new_position
< end
) { /* can't trim it zero or negative length */
809 framecnt_t newlen
= 0;
810 framepos_t delta
= 0;
812 if (!can_trim_start_before_source_start ()) {
813 /* can't trim it back past where source position zero is located */
814 new_position
= max (new_position
, source_zero
);
817 if (new_position
> _position
) {
818 newlen
= _length
- (new_position
- _position
);
819 delta
= -1 * (new_position
- _position
);
821 newlen
= _length
+ (_position
- new_position
);
822 delta
= _position
- new_position
;
825 trim_to_internal (new_position
, newlen
);
828 _right_of_split
= true;
831 if (!property_changes_suspended()) {
832 recompute_at_start ();
835 if (_transients
.size() > 0){
836 adjust_transients(delta
);
842 Region::modify_end (framepos_t new_endpoint
, bool reset_fade
)
848 if (new_endpoint
> _position
) {
849 trim_to_internal (_position
, new_endpoint
- _position
+1);
851 _left_of_split
= true;
853 if (!property_changes_suspended()) {
859 /** @param new_endpoint New region end point, such that, for example,
860 * a region at 0 of length 10 has an endpoint of 9.
864 Region::trim_end (framepos_t new_endpoint
)
866 modify_end (new_endpoint
, false);
870 Region::trim_to (framepos_t position
, framecnt_t length
)
876 trim_to_internal (position
, length
);
878 if (!property_changes_suspended()) {
879 recompute_at_start ();
885 Region::trim_to_internal (framepos_t position
, framecnt_t length
)
887 framepos_t new_start
;
893 frameoffset_t
const start_shift
= position
- _position
;
895 if (start_shift
> 0) {
897 if (_start
> max_framepos
- start_shift
) {
898 new_start
= max_framepos
;
900 new_start
= _start
+ start_shift
;
903 } else if (start_shift
< 0) {
905 if (_start
< -start_shift
&& !can_trim_start_before_source_start ()) {
908 new_start
= _start
+ start_shift
;
915 if (!verify_start_and_length (new_start
, length
)) {
919 PropertyChange what_changed
;
921 if (_start
!= new_start
) {
923 what_changed
.add (Properties::start
);
926 /* Set position before length, otherwise for MIDI regions this bad thing happens:
927 * 1. we call set_length_internal; length in beats is computed using the region's current
928 * (soon-to-be old) position
929 * 2. we call set_position_internal; position is set and length in frames re-computed using
930 * length in beats from (1) but at the new position, which is wrong if the region
931 * straddles a tempo/meter change.
934 if (_position
!= position
) {
935 if (!property_changes_suspended()) {
936 _last_position
= _position
;
938 set_position_internal (position
, true);
939 what_changed
.add (Properties::position
);
942 if (_length
!= length
) {
943 if (!property_changes_suspended()) {
944 _last_length
= _length
;
946 set_length_internal (length
);
947 what_changed
.add (Properties::length
);
952 PropertyChange start_and_length
;
954 start_and_length
.add (Properties::start
);
955 start_and_length
.add (Properties::length
);
957 if (what_changed
.contains (start_and_length
)) {
961 if (!what_changed
.empty()) {
962 send_change (what_changed
);
967 Region::set_hidden (bool yn
)
969 if (hidden() != yn
) {
971 send_change (Properties::hidden
);
976 Region::set_whole_file (bool yn
)
979 /* no change signal */
983 Region::set_automatic (bool yn
)
986 /* no change signal */
990 Region::set_muted (bool yn
)
994 send_change (Properties::muted
);
999 Region::set_opaque (bool yn
)
1001 if (opaque() != yn
) {
1003 send_change (Properties::opaque
);
1008 Region::set_locked (bool yn
)
1010 if (locked() != yn
) {
1012 send_change (Properties::locked
);
1017 Region::set_position_locked (bool yn
)
1019 if (position_locked() != yn
) {
1020 _position_locked
= yn
;
1021 send_change (Properties::locked
);
1025 /** Set the region's sync point.
1026 * @param absolute_pos Session time.
1029 Region::set_sync_position (framepos_t absolute_pos
)
1031 /* position within our file */
1032 framepos_t
const file_pos
= _start
+ (absolute_pos
- _position
);
1034 if (file_pos
!= _sync_position
) {
1035 _sync_marked
= true;
1036 _sync_position
= file_pos
;
1037 if (!property_changes_suspended()) {
1041 send_change (Properties::sync_position
);
1046 Region::clear_sync_position ()
1048 if (sync_marked()) {
1049 _sync_marked
= false;
1050 if (!property_changes_suspended()) {
1054 send_change (Properties::sync_position
);
1058 /* @return the sync point relative the first frame of the region */
1060 Region::sync_offset (int& dir
) const
1062 if (sync_marked()) {
1063 if (_sync_position
> _start
) {
1065 return _sync_position
- _start
;
1068 return _start
- _sync_position
;
1077 Region::adjust_to_sync (framepos_t pos
) const
1080 frameoffset_t offset
= sync_offset (sync_dir
);
1082 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1091 if (max_framepos
- pos
> offset
) {
1099 /** @return Sync position in session time */
1101 Region::sync_position() const
1103 if (sync_marked()) {
1104 return _position
- _start
+ _sync_position
;
1106 /* if sync has not been marked, use the start of the region */
1114 boost::shared_ptr
<Playlist
> pl (playlist());
1116 pl
->raise_region (shared_from_this ());
1123 boost::shared_ptr
<Playlist
> pl (playlist());
1125 pl
->lower_region (shared_from_this ());
1131 Region::raise_to_top ()
1133 boost::shared_ptr
<Playlist
> pl (playlist());
1135 pl
->raise_region_to_top (shared_from_this());
1140 Region::lower_to_bottom ()
1142 boost::shared_ptr
<Playlist
> pl (playlist());
1144 pl
->lower_region_to_bottom (shared_from_this());
1149 Region::set_layer (layer_t l
)
1154 send_change (Properties::layer
);
1161 XMLNode
*node
= new XMLNode ("Region");
1164 LocaleGuard
lg (X_("POSIX"));
1165 const char* fe
= NULL
;
1167 add_properties (*node
);
1169 _id
.print (buf
, sizeof (buf
));
1170 node
->add_property ("id", buf
);
1171 node
->add_property ("type", _type
.to_string());
1173 switch (_first_edit
) {
1174 case EditChangesNothing
:
1177 case EditChangesName
:
1183 default: /* should be unreachable but makes g++ happy */
1188 node
->add_property ("first-edit", fe
);
1190 /* note: flags are stored by derived classes */
1192 if (_position_lock_style
!= AudioTime
) {
1195 node
->add_property ("bbt-position", str
.str());
1198 for (uint32_t n
=0; n
< _sources
.size(); ++n
) {
1199 snprintf (buf2
, sizeof(buf2
), "source-%d", n
);
1200 _sources
[n
]->id().print (buf
, sizeof(buf
));
1201 node
->add_property (buf2
, buf
);
1204 for (uint32_t n
=0; n
< _master_sources
.size(); ++n
) {
1205 snprintf (buf2
, sizeof(buf2
), "master-source-%d", n
);
1206 _master_sources
[n
]->id().print (buf
, sizeof (buf
));
1207 node
->add_property (buf2
, buf
);
1210 if (max_source_level() > 0) {
1212 XMLNode
* nested_node
= new XMLNode (X_("NestedSource"));
1214 /* region is compound - get its playlist and
1215 store that before we list the region that
1219 for (SourceList::const_iterator s
= _sources
.begin(); s
!= _sources
.end(); ++s
) {
1220 nested_node
->add_child_nocopy ((*s
)->get_state ());
1224 node
->add_child_nocopy (*nested_node
);
1229 node
->add_child_copy (*_extra_xml
);
1236 Region::get_state ()
1242 Region::set_state (const XMLNode
& node
, int version
)
1244 PropertyChange what_changed
;
1245 return _set_state (node
, version
, what_changed
, true);
1249 Region::_set_state (const XMLNode
& node
, int /*version*/, PropertyChange
& what_changed
, bool send
)
1251 const XMLProperty
* prop
;
1253 Stateful::save_extra_xml (node
);
1255 what_changed
= set_values (node
);
1257 if ((prop
= node
.property (X_("id")))) {
1258 _id
= prop
->value();
1261 if (_position_lock_style
== MusicTime
) {
1262 if ((prop
= node
.property ("bbt-position")) == 0) {
1263 /* missing BBT info, revert to audio time locking */
1264 _position_lock_style
= AudioTime
;
1266 if (sscanf (prop
->value().c_str(), "%d|%d|%d",
1269 &_bbt_time
.ticks
) != 3) {
1270 _position_lock_style
= AudioTime
;
1275 /* fix problems with old sessions corrupted by impossible
1276 values for _stretch or _shift
1278 if (_stretch
== 0.0f
) {
1282 if (_shift
== 0.0f
) {
1287 send_change (what_changed
);
1290 /* Quick fix for 2.x sessions when region is muted */
1291 if ((prop
= node
.property (X_("flags")))) {
1292 if (string::npos
!= prop
->value().find("Muted")){
1302 Region::suspend_property_changes ()
1304 Stateful::suspend_property_changes ();
1305 _last_length
= _length
;
1306 _last_position
= _position
;
1310 Region::mid_thaw (const PropertyChange
& what_changed
)
1312 if (what_changed
.contains (Properties::length
)) {
1313 if (what_changed
.contains (Properties::position
)) {
1314 recompute_at_start ();
1316 recompute_at_end ();
1321 Region::send_change (const PropertyChange
& what_changed
)
1323 if (what_changed
.empty()) {
1327 Stateful::send_change (what_changed
);
1329 if (!Stateful::frozen()) {
1331 /* Try and send a shared_pointer unless this is part of the constructor.
1336 boost::shared_ptr
<Region
> rptr
= shared_from_this();
1337 RegionPropertyChanged (rptr
, what_changed
);
1339 /* no shared_ptr available, relax; */
1345 Region::set_last_layer_op (uint64_t when
)
1347 _last_layer_op
= when
;
1351 Region::overlap_equivalent (boost::shared_ptr
<const Region
> other
) const
1353 return coverage (other
->first_frame(), other
->last_frame()) != OverlapNone
;
1357 Region::equivalent (boost::shared_ptr
<const Region
> other
) const
1359 return _start
== other
->_start
&&
1360 _position
== other
->_position
&&
1361 _length
== other
->_length
;
1365 Region::size_equivalent (boost::shared_ptr
<const Region
> other
) const
1367 return _start
== other
->_start
&&
1368 _length
== other
->_length
;
1372 Region::region_list_equivalent (boost::shared_ptr
<const Region
> other
) const
1374 return size_equivalent (other
) && source_equivalent (other
) && _name
== other
->_name
;
1378 Region::source_deleted (boost::weak_ptr
<Source
>)
1382 if (!_session
.deletion_in_progress()) {
1383 /* this is a very special case: at least one of the region's
1384 sources has bee deleted, so invalidate all references to
1385 ourselves. Do NOT do this during session deletion, because
1386 then we run the risk that this will actually result
1387 in this object being deleted (as refcnt goes to zero)
1388 while emitting DropReferences.
1396 Region::master_source_names ()
1398 SourceList::iterator i
;
1400 vector
<string
> names
;
1401 for (i
= _master_sources
.begin(); i
!= _master_sources
.end(); ++i
) {
1402 names
.push_back((*i
)->name());
1409 Region::set_master_sources (const SourceList
& srcs
)
1411 for (SourceList::const_iterator i
= _master_sources
.begin (); i
!= _master_sources
.end(); ++i
) {
1412 (*i
)->dec_use_count ();
1415 _master_sources
= srcs
;
1416 assert (_sources
.size() == _master_sources
.size());
1418 for (SourceList::const_iterator i
= _master_sources
.begin (); i
!= _master_sources
.end(); ++i
) {
1419 (*i
)->inc_use_count ();
1424 Region::source_equivalent (boost::shared_ptr
<const Region
> other
) const
1429 if ((_sources
.size() != other
->_sources
.size()) ||
1430 (_master_sources
.size() != other
->_master_sources
.size())) {
1434 SourceList::const_iterator i
;
1435 SourceList::const_iterator io
;
1437 for (i
= _sources
.begin(), io
= other
->_sources
.begin(); i
!= _sources
.end() && io
!= other
->_sources
.end(); ++i
, ++io
) {
1438 if ((*i
)->id() != (*io
)->id()) {
1443 for (i
= _master_sources
.begin(), io
= other
->_master_sources
.begin(); i
!= _master_sources
.end() && io
!= other
->_master_sources
.end(); ++i
, ++io
) {
1444 if ((*i
)->id() != (*io
)->id()) {
1453 Region::source_string () const
1455 //string res = itos(_sources.size());
1458 res
<< _sources
.size() << ":";
1460 SourceList::const_iterator i
;
1462 for (i
= _sources
.begin(); i
!= _sources
.end(); ++i
) {
1463 res
<< (*i
)->id() << ":";
1466 for (i
= _master_sources
.begin(); i
!= _master_sources
.end(); ++i
) {
1467 res
<< (*i
)->id() << ":";
1474 Region::uses_source (boost::shared_ptr
<const Source
> source
) const
1476 for (SourceList::const_iterator i
= _sources
.begin(); i
!= _sources
.end(); ++i
) {
1485 Region::uses_source_path (const std::string
& path
) const
1487 for (SourceList::const_iterator i
= _sources
.begin(); i
!= _sources
.end(); ++i
) {
1488 boost::shared_ptr
<const FileSource
> fs
= boost::dynamic_pointer_cast
<const FileSource
> (*i
);
1490 if (fs
->path() == path
) {
1499 Region::source_length(uint32_t n
) const
1501 assert (n
< _sources
.size());
1502 return _sources
[n
]->length (_position
- _start
);
1506 Region::verify_length (framecnt_t len
)
1508 if (source() && (source()->destructive() || source()->length_mutable())) {
1512 framecnt_t maxlen
= 0;
1514 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1515 maxlen
= max (maxlen
, source_length(n
) - _start
);
1518 len
= min (len
, maxlen
);
1524 Region::verify_start_and_length (framepos_t new_start
, framecnt_t
& new_length
)
1526 if (source() && (source()->destructive() || source()->length_mutable())) {
1530 framecnt_t maxlen
= 0;
1532 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1533 maxlen
= max (maxlen
, source_length(n
) - new_start
);
1536 new_length
= min (new_length
, maxlen
);
1542 Region::verify_start (framepos_t pos
)
1544 if (source() && (source()->destructive() || source()->length_mutable())) {
1548 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1549 if (pos
> source_length(n
) - _length
) {
1557 Region::verify_start_mutable (framepos_t
& new_start
)
1559 if (source() && (source()->destructive() || source()->length_mutable())) {
1563 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1564 if (new_start
> source_length(n
) - _length
) {
1565 new_start
= source_length(n
) - _length
;
1571 boost::shared_ptr
<Region
>
1572 Region::get_parent() const
1574 boost::shared_ptr
<Playlist
> pl (playlist());
1577 boost::shared_ptr
<Region
> r
;
1578 boost::shared_ptr
<Region
const> grrr2
= boost::dynamic_pointer_cast
<Region
const> (shared_from_this());
1580 if (grrr2
&& (r
= _session
.find_whole_file_parent (grrr2
))) {
1581 return boost::static_pointer_cast
<Region
> (r
);
1585 return boost::shared_ptr
<Region
>();
1589 Region::apply (Filter
& filter
, Progress
* progress
)
1591 return filter
.run (shared_from_this(), progress
);
1596 Region::invalidate_transients ()
1598 _valid_transients
= false;
1599 _transients
.clear ();
1601 send_change (PropertyChange (Properties::valid_transients
));
1605 Region::drop_sources ()
1607 for (SourceList::const_iterator i
= _sources
.begin (); i
!= _sources
.end(); ++i
) {
1608 (*i
)->dec_use_count ();
1613 for (SourceList::const_iterator i
= _master_sources
.begin (); i
!= _master_sources
.end(); ++i
) {
1614 (*i
)->dec_use_count ();
1617 _master_sources
.clear ();
1621 Region::use_sources (SourceList
const & s
)
1623 set
<boost::shared_ptr
<Source
> > unique_srcs
;
1625 for (SourceList::const_iterator i
= s
.begin (); i
!= s
.end(); ++i
) {
1627 _sources
.push_back (*i
);
1628 (*i
)->inc_use_count ();
1629 _master_sources
.push_back (*i
);
1630 (*i
)->inc_use_count ();
1632 /* connect only once to DropReferences, even if sources are replicated
1635 if (unique_srcs
.find (*i
) == unique_srcs
.end ()) {
1636 unique_srcs
.insert (*i
);
1637 (*i
)->DropReferences
.connect_same_thread (*this, boost::bind (&Region::source_deleted
, this, boost::weak_ptr
<Source
>(*i
)));
1643 Region::can_trim () const
1645 CanTrim ct
= CanTrim (0);
1651 /* if not locked, we can always move the front later, and the end earlier
1654 ct
= CanTrim (ct
| FrontTrimLater
| EndTrimEarlier
);
1656 if (start() != 0 || can_trim_start_before_source_start ()) {
1657 ct
= CanTrim (ct
| FrontTrimEarlier
);
1660 if (!_sources
.empty()) {
1661 if ((start() + length()) < _sources
.front()->length (0)) {
1662 ct
= CanTrim (ct
| EndTrimLater
);
1670 Region::max_source_level () const
1674 for (SourceList::const_iterator i
= _sources
.begin(); i
!= _sources
.end(); ++i
) {
1675 lvl
= max (lvl
, (*i
)->level());
1682 Region::is_compound () const
1684 return max_source_level() > 0;