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/playlist_source.h"
36 #include "ardour/profile.h"
37 #include "ardour/region.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/session.h"
40 #include "ardour/source.h"
41 #include "ardour/source_factory.h"
42 #include "ardour/tempo.h"
43 #include "ardour/utils.h"
48 using namespace ARDOUR
;
52 namespace Properties
{
53 PBD::PropertyDescriptor
<bool> muted
;
54 PBD::PropertyDescriptor
<bool> opaque
;
55 PBD::PropertyDescriptor
<bool> locked
;
56 PBD::PropertyDescriptor
<bool> automatic
;
57 PBD::PropertyDescriptor
<bool> whole_file
;
58 PBD::PropertyDescriptor
<bool> import
;
59 PBD::PropertyDescriptor
<bool> external
;
60 PBD::PropertyDescriptor
<bool> sync_marked
;
61 PBD::PropertyDescriptor
<bool> left_of_split
;
62 PBD::PropertyDescriptor
<bool> right_of_split
;
63 PBD::PropertyDescriptor
<bool> hidden
;
64 PBD::PropertyDescriptor
<bool> position_locked
;
65 PBD::PropertyDescriptor
<bool> valid_transients
;
66 PBD::PropertyDescriptor
<framepos_t
> start
;
67 PBD::PropertyDescriptor
<framecnt_t
> length
;
68 PBD::PropertyDescriptor
<framepos_t
> position
;
69 PBD::PropertyDescriptor
<framecnt_t
> sync_position
;
70 PBD::PropertyDescriptor
<layer_t
> layer
;
71 PBD::PropertyDescriptor
<framepos_t
> ancestral_start
;
72 PBD::PropertyDescriptor
<framecnt_t
> ancestral_length
;
73 PBD::PropertyDescriptor
<float> stretch
;
74 PBD::PropertyDescriptor
<float> shift
;
75 PBD::PropertyDescriptor
<PositionLockStyle
> position_lock_style
;
79 PBD::Signal2
<void,boost::shared_ptr
<ARDOUR::Region
>,const PropertyChange
&> Region::RegionPropertyChanged
;
82 Region::make_property_quarks ()
84 Properties::muted
.property_id
= g_quark_from_static_string (X_("muted"));
85 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for muted = %1\n", Properties::muted
.property_id
));
86 Properties::opaque
.property_id
= g_quark_from_static_string (X_("opaque"));
87 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for opaque = %1\n", Properties::opaque
.property_id
));
88 Properties::locked
.property_id
= g_quark_from_static_string (X_("locked"));
89 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for locked = %1\n", Properties::locked
.property_id
));
90 Properties::automatic
.property_id
= g_quark_from_static_string (X_("automatic"));
91 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for automatic = %1\n", Properties::automatic
.property_id
));
92 Properties::whole_file
.property_id
= g_quark_from_static_string (X_("whole-file"));
93 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for whole-file = %1\n", Properties::whole_file
.property_id
));
94 Properties::import
.property_id
= g_quark_from_static_string (X_("import"));
95 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for import = %1\n", Properties::import
.property_id
));
96 Properties::external
.property_id
= g_quark_from_static_string (X_("external"));
97 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for external = %1\n", Properties::external
.property_id
));
98 Properties::sync_marked
.property_id
= g_quark_from_static_string (X_("sync-marked"));
99 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked
.property_id
));
100 Properties::left_of_split
.property_id
= g_quark_from_static_string (X_("left-of-split"));
101 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split
.property_id
));
102 Properties::right_of_split
.property_id
= g_quark_from_static_string (X_("right-of-split"));
103 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split
.property_id
));
104 Properties::hidden
.property_id
= g_quark_from_static_string (X_("hidden"));
105 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for hidden = %1\n", Properties::hidden
.property_id
));
106 Properties::position_locked
.property_id
= g_quark_from_static_string (X_("position-locked"));
107 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for position-locked = %1\n", Properties::position_locked
.property_id
));
108 Properties::valid_transients
.property_id
= g_quark_from_static_string (X_("valid-transients"));
109 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients
.property_id
));
110 Properties::start
.property_id
= g_quark_from_static_string (X_("start"));
111 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for start = %1\n", Properties::start
.property_id
));
112 Properties::length
.property_id
= g_quark_from_static_string (X_("length"));
113 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for length = %1\n", Properties::length
.property_id
));
114 Properties::position
.property_id
= g_quark_from_static_string (X_("position"));
115 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for position = %1\n", Properties::position
.property_id
));
116 Properties::sync_position
.property_id
= g_quark_from_static_string (X_("sync-position"));
117 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for sync-position = %1\n", Properties::sync_position
.property_id
));
118 Properties::layer
.property_id
= g_quark_from_static_string (X_("layer"));
119 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for layer = %1\n", Properties::layer
.property_id
));
120 Properties::ancestral_start
.property_id
= g_quark_from_static_string (X_("ancestral-start"));
121 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start
.property_id
));
122 Properties::ancestral_length
.property_id
= g_quark_from_static_string (X_("ancestral-length"));
123 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length
.property_id
));
124 Properties::stretch
.property_id
= g_quark_from_static_string (X_("stretch"));
125 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for stretch = %1\n", Properties::stretch
.property_id
));
126 Properties::shift
.property_id
= g_quark_from_static_string (X_("shift"));
127 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for shift = %1\n", Properties::shift
.property_id
));
128 Properties::position_lock_style
.property_id
= g_quark_from_static_string (X_("positional-lock-style"));
129 DEBUG_TRACE (DEBUG::Properties
, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style
.property_id
));
133 Region::register_properties ()
135 _xml_node_name
= X_("Region");
137 add_property (_muted
);
138 add_property (_opaque
);
139 add_property (_locked
);
140 add_property (_automatic
);
141 add_property (_whole_file
);
142 add_property (_import
);
143 add_property (_external
);
144 add_property (_sync_marked
);
145 add_property (_left_of_split
);
146 add_property (_right_of_split
);
147 add_property (_hidden
);
148 add_property (_position_locked
);
149 add_property (_valid_transients
);
150 add_property (_start
);
151 add_property (_length
);
152 add_property (_position
);
153 add_property (_sync_position
);
154 add_property (_layer
);
155 add_property (_ancestral_start
);
156 add_property (_ancestral_length
);
157 add_property (_stretch
);
158 add_property (_shift
);
159 add_property (_position_lock_style
);
162 #define REGION_DEFAULT_STATE(s,l) \
163 _muted (Properties::muted, false) \
164 , _opaque (Properties::opaque, true) \
165 , _locked (Properties::locked, false) \
166 , _automatic (Properties::automatic, false) \
167 , _whole_file (Properties::whole_file, false) \
168 , _import (Properties::import, false) \
169 , _external (Properties::external, false) \
170 , _sync_marked (Properties::sync_marked, false) \
171 , _left_of_split (Properties::left_of_split, false) \
172 , _right_of_split (Properties::right_of_split, false) \
173 , _hidden (Properties::hidden, false) \
174 , _position_locked (Properties::position_locked, false) \
175 , _valid_transients (Properties::valid_transients, false) \
176 , _start (Properties::start, (s)) \
177 , _length (Properties::length, (l)) \
178 , _position (Properties::position, 0) \
179 , _sync_position (Properties::sync_position, (s)) \
180 , _layer (Properties::layer, 0) \
181 , _ancestral_start (Properties::ancestral_start, (s)) \
182 , _ancestral_length (Properties::ancestral_length, (l)) \
183 , _stretch (Properties::stretch, 1.0) \
184 , _shift (Properties::shift, 1.0) \
185 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
187 #define REGION_COPY_STATE(other) \
188 _muted (Properties::muted, other->_muted) \
189 , _opaque (Properties::opaque, other->_opaque) \
190 , _locked (Properties::locked, other->_locked) \
191 , _automatic (Properties::automatic, other->_automatic) \
192 , _whole_file (Properties::whole_file, other->_whole_file) \
193 , _import (Properties::import, other->_import) \
194 , _external (Properties::external, other->_external) \
195 , _sync_marked (Properties::sync_marked, other->_sync_marked) \
196 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
197 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
198 , _hidden (Properties::hidden, other->_hidden) \
199 , _position_locked (Properties::position_locked, other->_position_locked) \
200 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
201 , _start(Properties::start, other->_start) \
202 , _length(Properties::length, other->_length) \
203 , _position(Properties::position, other->_position) \
204 , _sync_position(Properties::sync_position, other->_sync_position) \
205 , _layer (Properties::layer, other->_layer) \
206 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
207 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
208 , _stretch (Properties::stretch, other->_stretch) \
209 , _shift (Properties::shift, other->_shift) \
210 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
212 /* derived-from-derived constructor (no sources in constructor) */
213 Region::Region (Session
& s
, framepos_t start
, framecnt_t length
, const string
& name
, DataType type
)
214 : SessionObject(s
, name
)
216 , REGION_DEFAULT_STATE(start
,length
)
217 , _last_length (length
)
219 , _first_edit (EditChangesNothing
)
220 , _read_data_count(0)
222 , _pending_explicit_relayer (false)
224 register_properties ();
226 /* no sources at this point */
229 /** Basic Region constructor (many sources) */
230 Region::Region (const SourceList
& srcs
)
231 : SessionObject(srcs
.front()->session(), "toBeRenamed")
232 , _type (srcs
.front()->type())
233 , REGION_DEFAULT_STATE(0,0)
236 , _first_edit (EditChangesNothing
)
237 , _read_data_count(0)
239 , _pending_explicit_relayer (false)
241 register_properties ();
243 _type
= srcs
.front()->type();
247 assert(_sources
.size() > 0);
248 assert (_type
== srcs
.front()->type());
251 /** Create a new Region from an existing one */
252 Region::Region (boost::shared_ptr
<const Region
> other
)
253 : SessionObject(other
->session(), other
->name())
254 , _type (other
->data_type())
255 , REGION_COPY_STATE (other
)
256 , _last_length (other
->_last_length
)
257 , _last_position(other
->_last_position
) \
258 , _first_edit (EditChangesNothing
)
259 , _read_data_count(0)
261 , _pending_explicit_relayer (false)
264 register_properties ();
266 /* override state that may have been incorrectly inherited from the other region
274 use_sources (other
->_sources
);
276 _position_lock_style
= other
->_position_lock_style
;
277 _first_edit
= other
->_first_edit
;
279 _start
= 0; // It seems strange _start is not inherited here?
281 /* sync pos is relative to start of file. our start-in-file is now zero,
282 so set our sync position to whatever the the difference between
283 _start and _sync_pos was in the other region.
285 result is that our new sync pos points to the same point in our source(s)
286 as the sync in the other region did in its source(s).
288 since we start at zero in our source(s), it is not possible to use a sync point that
289 is before the start. reset it to _start if that was true in the other region.
292 if (other
->sync_marked()) {
293 if (other
->_start
< other
->_sync_position
) {
294 /* sync pos was after the start point of the other region */
295 _sync_position
= other
->_sync_position
- other
->_start
;
297 /* sync pos was before the start point of the other region. not possible here. */
298 _sync_marked
= false;
299 _sync_position
= _start
;
302 _sync_marked
= false;
303 _sync_position
= _start
;
306 if (Profile
->get_sae()) {
307 /* reset sync point to start if its ended up
308 outside region bounds.
311 if (_sync_position
< _start
|| _sync_position
>= _start
+ _length
) {
312 _sync_marked
= false;
313 _sync_position
= _start
;
317 assert (_type
== other
->data_type());
320 /** Create a new Region from part of an existing one.
322 the start within \a other is given by \a offset
323 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
325 Region::Region (boost::shared_ptr
<const Region
> other
, frameoffset_t offset
)
326 : SessionObject(other
->session(), other
->name())
327 , _type (other
->data_type())
328 , REGION_COPY_STATE (other
)
329 , _last_length (other
->_last_length
)
330 , _last_position(other
->_last_position
) \
331 , _first_edit (EditChangesNothing
)
332 , _read_data_count(0)
334 , _pending_explicit_relayer (false)
337 register_properties ();
339 /* override state that may have been incorrectly inherited from the other region
347 use_sources (other
->_sources
);
349 _start
= other
->_start
+ offset
;
351 /* if the other region had a distinct sync point
352 set, then continue to use it as best we can.
353 otherwise, reset sync point back to start.
356 if (other
->sync_marked()) {
357 if (other
->_sync_position
< _start
) {
358 _sync_marked
= false;
359 _sync_position
= _start
;
361 _sync_position
= other
->_sync_position
;
364 _sync_marked
= false;
365 _sync_position
= _start
;
368 if (Profile
->get_sae()) {
369 /* reset sync point to start if its ended up
370 outside region bounds.
373 if (_sync_position
< _start
|| _sync_position
>= _start
+ _length
) {
374 _sync_marked
= false;
375 _sync_position
= _start
;
379 assert (_type
== other
->data_type());
382 /** Create a copy of @param other but with different sources. Used by filters */
383 Region::Region (boost::shared_ptr
<const Region
> other
, const SourceList
& srcs
)
384 : SessionObject (other
->session(), other
->name())
385 , _type (srcs
.front()->type())
386 , REGION_COPY_STATE (other
)
387 , _last_length (other
->_last_length
)
388 , _last_position (other
->_last_position
)
389 , _first_edit (EditChangesID
)
390 , _read_data_count (0)
391 , _last_layer_op (other
->_last_layer_op
)
392 , _pending_explicit_relayer (false)
394 register_properties ();
397 _position_locked
= false;
399 other
->_first_edit
= EditChangesName
;
401 if (other
->_extra_xml
) {
402 _extra_xml
= new XMLNode (*other
->_extra_xml
);
408 assert(_sources
.size() > 0);
413 DEBUG_TRACE (DEBUG::Destruction
, string_compose ("Region %1 destructor @ %2\n", _name
, this));
418 Region::set_playlist (boost::weak_ptr
<Playlist
> wpl
)
420 _playlist
= wpl
.lock();
424 Region::set_name (const std::string
& str
)
427 SessionObject::set_name(str
); // EMIT SIGNAL NameChanged()
428 assert(_name
== str
);
430 send_change (Properties::name
);
437 Region::set_length (framecnt_t len
)
439 //cerr << "Region::set_length() len = " << len << endl;
444 if (_length
!= len
&& len
!= 0) {
446 /* check that the current _position wouldn't make the new
450 if (max_framepos
- len
< _position
) {
454 if (!verify_length (len
)) {
459 _last_length
= _length
;
460 set_length_internal (len
);
464 invalidate_transients ();
466 if (!property_changes_suspended()) {
470 send_change (Properties::length
);
475 Region::set_length_internal (framecnt_t len
)
481 Region::maybe_uncopy ()
483 /* this does nothing but marked a semantic moment once upon a time */
487 Region::first_edit ()
489 boost::shared_ptr
<Playlist
> pl (playlist());
491 if (_first_edit
!= EditChangesNothing
&& pl
) {
493 _name
= RegionFactory::new_region_name (_name
);
494 _first_edit
= EditChangesNothing
;
496 send_change (Properties::name
);
498 RegionFactory::CheckNewRegion (shared_from_this());
503 Region::at_natural_position () const
505 boost::shared_ptr
<Playlist
> pl (playlist());
511 boost::shared_ptr
<Region
> whole_file_region
= get_parent();
513 if (whole_file_region
) {
514 if (_position
== whole_file_region
->position() + _start
) {
523 Region::move_to_natural_position ()
525 boost::shared_ptr
<Playlist
> pl (playlist());
531 boost::shared_ptr
<Region
> whole_file_region
= get_parent();
533 if (whole_file_region
) {
534 set_position (whole_file_region
->position() + _start
);
539 Region::special_set_position (framepos_t pos
)
541 /* this is used when creating a whole file region as
542 a way to store its "natural" or "captured" position.
545 _position
= _position
;
550 Region::set_position_lock_style (PositionLockStyle ps
)
552 if (_position_lock_style
!= ps
) {
554 boost::shared_ptr
<Playlist
> pl (playlist());
560 _position_lock_style
= ps
;
562 if (_position_lock_style
== MusicTime
) {
563 _session
.tempo_map().bbt_time (_position
, _bbt_time
);
566 send_change (Properties::position_lock_style
);
572 Region::update_position_after_tempo_map_change ()
574 boost::shared_ptr
<Playlist
> pl (playlist());
576 if (!pl
|| _position_lock_style
!= MusicTime
) {
580 TempoMap
& map (_session
.tempo_map());
581 framepos_t pos
= map
.frame_time (_bbt_time
);
582 set_position_internal (pos
, false);
584 /* do this even if the position is the same. this helps out
585 a GUI that has moved its representation already.
587 send_change (Properties::position
);
591 Region::set_position (framepos_t pos
)
597 set_position_internal (pos
, true);
599 /* do this even if the position is the same. this helps out
600 a GUI that has moved its representation already.
602 send_change (Properties::position
);
607 Region::set_position_internal (framepos_t pos
, bool allow_bbt_recompute
)
609 if (_position
!= pos
) {
610 _last_position
= _position
;
613 /* check that the new _position wouldn't make the current
614 length impossible - if so, change the length.
616 XXX is this the right thing to do?
619 if (max_framepos
- _length
< _position
) {
620 _last_length
= _length
;
621 _length
= max_framepos
- _position
;
624 if (allow_bbt_recompute
) {
625 recompute_position_from_lock_style ();
628 //invalidate_transients ();
633 Region::set_position_on_top (framepos_t pos
)
639 if (_position
!= pos
) {
640 set_position_internal (pos
, true);
643 boost::shared_ptr
<Playlist
> pl (playlist());
646 pl
->raise_region_to_top (shared_from_this ());
649 /* do this even if the position is the same. this helps out
650 a GUI that has moved its representation already.
652 send_change (Properties::position
);
656 Region::recompute_position_from_lock_style ()
658 if (_position_lock_style
== MusicTime
) {
659 _session
.tempo_map().bbt_time (_position
, _bbt_time
);
664 Region::nudge_position (frameoffset_t n
)
674 framepos_t new_position
= _position
;
677 if (_position
> max_framepos
- n
) {
678 new_position
= max_framepos
;
683 if (_position
< -n
) {
690 set_position_internal (new_position
, true);
692 send_change (Properties::position
);
696 Region::set_ancestral_data (framepos_t s
, framecnt_t l
, float st
, float sh
)
698 _ancestral_length
= l
;
699 _ancestral_start
= s
;
705 Region::set_start (framepos_t pos
)
707 if (locked() || position_locked()) {
710 /* This just sets the start, nothing else. It effectively shifts
711 the contents of the Region within the overall extent of the Source,
712 without changing the Region's position or length
717 if (!verify_start (pos
)) {
724 invalidate_transients ();
726 send_change (Properties::start
);
731 Region::trim_start (framepos_t new_position
)
733 if (locked() || position_locked()) {
736 framepos_t new_start
;
737 frameoffset_t
const start_shift
= new_position
- _position
;
739 if (start_shift
> 0) {
741 if (_start
> max_framepos
- start_shift
) {
742 new_start
= max_framepos
;
744 new_start
= _start
+ start_shift
;
747 if (!verify_start (new_start
)) {
751 } else if (start_shift
< 0) {
753 if (_start
< -start_shift
) {
756 new_start
= _start
+ start_shift
;
763 if (new_start
== _start
) {
771 send_change (Properties::start
);
775 Region::trim_front (framepos_t new_position
)
777 modify_front (new_position
, false);
781 Region::cut_front (framepos_t new_position
)
783 modify_front (new_position
, true);
787 Region::cut_end (framepos_t new_endpoint
)
789 modify_end (new_endpoint
, true);
793 Region::modify_front (framepos_t new_position
, bool reset_fade
)
799 framepos_t end
= last_frame();
800 framepos_t source_zero
;
802 if (_position
> _start
) {
803 source_zero
= _position
- _start
;
805 source_zero
= 0; // its actually negative, but this will work for us
808 if (new_position
< end
) { /* can't trim it zero or negative length */
810 framecnt_t newlen
= 0;
811 framepos_t delta
= 0;
813 if (!can_trim_start_before_source_start ()) {
814 /* can't trim it back past where source position zero is located */
815 new_position
= max (new_position
, source_zero
);
818 if (new_position
> _position
) {
819 newlen
= _length
- (new_position
- _position
);
820 delta
= -1 * (new_position
- _position
);
822 newlen
= _length
+ (_position
- new_position
);
823 delta
= _position
- new_position
;
826 trim_to_internal (new_position
, newlen
);
829 _right_of_split
= true;
832 if (!property_changes_suspended()) {
833 recompute_at_start ();
836 if (_transients
.size() > 0){
837 adjust_transients(delta
);
843 Region::modify_end (framepos_t new_endpoint
, bool reset_fade
)
849 if (new_endpoint
> _position
) {
850 trim_to_internal (_position
, new_endpoint
- _position
+1);
852 _left_of_split
= true;
854 if (!property_changes_suspended()) {
860 /** @param new_endpoint New region end point, such that, for example,
861 * a region at 0 of length 10 has an endpoint of 9.
865 Region::trim_end (framepos_t new_endpoint
)
867 modify_end (new_endpoint
, false);
871 Region::trim_to (framepos_t position
, framecnt_t length
)
877 trim_to_internal (position
, length
);
879 if (!property_changes_suspended()) {
880 recompute_at_start ();
886 Region::trim_to_internal (framepos_t position
, framecnt_t length
)
888 framepos_t new_start
;
894 frameoffset_t
const start_shift
= position
- _position
;
896 if (start_shift
> 0) {
898 if (_start
> max_framepos
- start_shift
) {
899 new_start
= max_framepos
;
901 new_start
= _start
+ start_shift
;
904 } else if (start_shift
< 0) {
906 if (_start
< -start_shift
&& !can_trim_start_before_source_start ()) {
909 new_start
= _start
+ start_shift
;
916 if (!verify_start_and_length (new_start
, length
)) {
920 PropertyChange what_changed
;
922 if (_start
!= new_start
) {
924 what_changed
.add (Properties::start
);
927 /* Set position before length, otherwise for MIDI regions this bad thing happens:
928 * 1. we call set_length_internal; length in beats is computed using the region's current
929 * (soon-to-be old) position
930 * 2. we call set_position_internal; position is set and length in frames re-computed using
931 * length in beats from (1) but at the new position, which is wrong if the region
932 * straddles a tempo/meter change.
935 if (_position
!= position
) {
936 if (!property_changes_suspended()) {
937 _last_position
= _position
;
939 set_position_internal (position
, true);
940 what_changed
.add (Properties::position
);
943 if (_length
!= length
) {
944 if (!property_changes_suspended()) {
945 _last_length
= _length
;
947 set_length_internal (length
);
948 what_changed
.add (Properties::length
);
953 PropertyChange start_and_length
;
955 start_and_length
.add (Properties::start
);
956 start_and_length
.add (Properties::length
);
958 if (what_changed
.contains (start_and_length
)) {
962 if (!what_changed
.empty()) {
963 send_change (what_changed
);
968 Region::set_hidden (bool yn
)
970 if (hidden() != yn
) {
972 send_change (Properties::hidden
);
977 Region::set_whole_file (bool yn
)
980 /* no change signal */
984 Region::set_automatic (bool yn
)
987 /* no change signal */
991 Region::set_muted (bool yn
)
995 send_change (Properties::muted
);
1000 Region::set_opaque (bool yn
)
1002 if (opaque() != yn
) {
1004 send_change (Properties::opaque
);
1009 Region::set_locked (bool yn
)
1011 if (locked() != yn
) {
1013 send_change (Properties::locked
);
1018 Region::set_position_locked (bool yn
)
1020 if (position_locked() != yn
) {
1021 _position_locked
= yn
;
1022 send_change (Properties::locked
);
1026 /** Set the region's sync point.
1027 * @param absolute_pos Session time.
1030 Region::set_sync_position (framepos_t absolute_pos
)
1032 /* position within our file */
1033 framepos_t
const file_pos
= _start
+ (absolute_pos
- _position
);
1035 if (file_pos
!= _sync_position
) {
1036 _sync_marked
= true;
1037 _sync_position
= file_pos
;
1038 if (!property_changes_suspended()) {
1042 send_change (Properties::sync_position
);
1047 Region::clear_sync_position ()
1049 if (sync_marked()) {
1050 _sync_marked
= false;
1051 if (!property_changes_suspended()) {
1055 send_change (Properties::sync_position
);
1059 /* @return the sync point relative the first frame of the region */
1061 Region::sync_offset (int& dir
) const
1063 if (sync_marked()) {
1064 if (_sync_position
> _start
) {
1066 return _sync_position
- _start
;
1069 return _start
- _sync_position
;
1078 Region::adjust_to_sync (framepos_t pos
) const
1081 frameoffset_t offset
= sync_offset (sync_dir
);
1083 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1092 if (max_framepos
- pos
> offset
) {
1100 /** @return Sync position in session time */
1102 Region::sync_position() const
1104 if (sync_marked()) {
1105 return _position
- _start
+ _sync_position
;
1107 /* if sync has not been marked, use the start of the region */
1115 boost::shared_ptr
<Playlist
> pl (playlist());
1117 pl
->raise_region (shared_from_this ());
1124 boost::shared_ptr
<Playlist
> pl (playlist());
1126 pl
->lower_region (shared_from_this ());
1132 Region::raise_to_top ()
1134 boost::shared_ptr
<Playlist
> pl (playlist());
1136 pl
->raise_region_to_top (shared_from_this());
1141 Region::lower_to_bottom ()
1143 boost::shared_ptr
<Playlist
> pl (playlist());
1145 pl
->lower_region_to_bottom (shared_from_this());
1150 Region::set_layer (layer_t l
)
1155 send_change (Properties::layer
);
1162 XMLNode
*node
= new XMLNode ("Region");
1165 LocaleGuard
lg (X_("POSIX"));
1166 const char* fe
= NULL
;
1168 add_properties (*node
);
1170 _id
.print (buf
, sizeof (buf
));
1171 node
->add_property ("id", buf
);
1172 node
->add_property ("type", _type
.to_string());
1174 switch (_first_edit
) {
1175 case EditChangesNothing
:
1178 case EditChangesName
:
1184 default: /* should be unreachable but makes g++ happy */
1189 node
->add_property ("first-edit", fe
);
1191 /* note: flags are stored by derived classes */
1193 if (_position_lock_style
!= AudioTime
) {
1196 node
->add_property ("bbt-position", str
.str());
1199 for (uint32_t n
=0; n
< _sources
.size(); ++n
) {
1200 snprintf (buf2
, sizeof(buf2
), "source-%d", n
);
1201 _sources
[n
]->id().print (buf
, sizeof(buf
));
1202 node
->add_property (buf2
, buf
);
1205 for (uint32_t n
=0; n
< _master_sources
.size(); ++n
) {
1206 snprintf (buf2
, sizeof(buf2
), "master-source-%d", n
);
1207 _master_sources
[n
]->id().print (buf
, sizeof (buf
));
1208 node
->add_property (buf2
, buf
);
1211 /* Only store nested sources for the whole-file region that acts
1212 as the parent/root of all regions using it.
1215 if (_whole_file
&& max_source_level() > 0) {
1217 XMLNode
* nested_node
= new XMLNode (X_("NestedSource"));
1219 /* region is compound - get its playlist and
1220 store that before we list the region that
1224 for (SourceList::const_iterator s
= _sources
.begin(); s
!= _sources
.end(); ++s
) {
1225 nested_node
->add_child_nocopy ((*s
)->get_state ());
1229 node
->add_child_nocopy (*nested_node
);
1234 node
->add_child_copy (*_extra_xml
);
1241 Region::get_state ()
1247 Region::set_state (const XMLNode
& node
, int version
)
1249 PropertyChange what_changed
;
1250 return _set_state (node
, version
, what_changed
, true);
1254 Region::_set_state (const XMLNode
& node
, int /*version*/, PropertyChange
& what_changed
, bool send
)
1256 const XMLProperty
* prop
;
1258 Stateful::save_extra_xml (node
);
1260 what_changed
= set_values (node
);
1262 if ((prop
= node
.property (X_("id")))) {
1263 _id
= prop
->value();
1266 if (_position_lock_style
== MusicTime
) {
1267 if ((prop
= node
.property ("bbt-position")) == 0) {
1268 /* missing BBT info, revert to audio time locking */
1269 _position_lock_style
= AudioTime
;
1271 if (sscanf (prop
->value().c_str(), "%d|%d|%d",
1274 &_bbt_time
.ticks
) != 3) {
1275 _position_lock_style
= AudioTime
;
1280 /* fix problems with old sessions corrupted by impossible
1281 values for _stretch or _shift
1283 if (_stretch
== 0.0f
) {
1287 if (_shift
== 0.0f
) {
1292 send_change (what_changed
);
1295 /* Quick fix for 2.x sessions when region is muted */
1296 if ((prop
= node
.property (X_("flags")))) {
1297 if (string::npos
!= prop
->value().find("Muted")){
1307 Region::suspend_property_changes ()
1309 Stateful::suspend_property_changes ();
1310 _last_length
= _length
;
1311 _last_position
= _position
;
1315 Region::mid_thaw (const PropertyChange
& what_changed
)
1317 if (what_changed
.contains (Properties::length
)) {
1318 if (what_changed
.contains (Properties::position
)) {
1319 recompute_at_start ();
1321 recompute_at_end ();
1326 Region::send_change (const PropertyChange
& what_changed
)
1328 if (what_changed
.empty()) {
1332 Stateful::send_change (what_changed
);
1334 if (!Stateful::frozen()) {
1336 /* Try and send a shared_pointer unless this is part of the constructor.
1341 boost::shared_ptr
<Region
> rptr
= shared_from_this();
1342 RegionPropertyChanged (rptr
, what_changed
);
1344 /* no shared_ptr available, relax; */
1350 Region::set_last_layer_op (uint64_t when
)
1352 _last_layer_op
= when
;
1356 Region::overlap_equivalent (boost::shared_ptr
<const Region
> other
) const
1358 return coverage (other
->first_frame(), other
->last_frame()) != OverlapNone
;
1362 Region::equivalent (boost::shared_ptr
<const Region
> other
) const
1364 return _start
== other
->_start
&&
1365 _position
== other
->_position
&&
1366 _length
== other
->_length
;
1370 Region::size_equivalent (boost::shared_ptr
<const Region
> other
) const
1372 return _start
== other
->_start
&&
1373 _length
== other
->_length
;
1377 Region::region_list_equivalent (boost::shared_ptr
<const Region
> other
) const
1379 return size_equivalent (other
) && source_equivalent (other
) && _name
== other
->_name
;
1383 Region::source_deleted (boost::weak_ptr
<Source
>)
1387 if (!_session
.deletion_in_progress()) {
1388 /* this is a very special case: at least one of the region's
1389 sources has bee deleted, so invalidate all references to
1390 ourselves. Do NOT do this during session deletion, because
1391 then we run the risk that this will actually result
1392 in this object being deleted (as refcnt goes to zero)
1393 while emitting DropReferences.
1401 Region::master_source_names ()
1403 SourceList::iterator i
;
1405 vector
<string
> names
;
1406 for (i
= _master_sources
.begin(); i
!= _master_sources
.end(); ++i
) {
1407 names
.push_back((*i
)->name());
1414 Region::set_master_sources (const SourceList
& srcs
)
1416 for (SourceList::const_iterator i
= _master_sources
.begin (); i
!= _master_sources
.end(); ++i
) {
1417 (*i
)->dec_use_count ();
1420 _master_sources
= srcs
;
1421 assert (_sources
.size() == _master_sources
.size());
1423 for (SourceList::const_iterator i
= _master_sources
.begin (); i
!= _master_sources
.end(); ++i
) {
1424 (*i
)->inc_use_count ();
1429 Region::source_equivalent (boost::shared_ptr
<const Region
> other
) const
1434 if ((_sources
.size() != other
->_sources
.size()) ||
1435 (_master_sources
.size() != other
->_master_sources
.size())) {
1439 SourceList::const_iterator i
;
1440 SourceList::const_iterator io
;
1442 for (i
= _sources
.begin(), io
= other
->_sources
.begin(); i
!= _sources
.end() && io
!= other
->_sources
.end(); ++i
, ++io
) {
1443 if ((*i
)->id() != (*io
)->id()) {
1448 for (i
= _master_sources
.begin(), io
= other
->_master_sources
.begin(); i
!= _master_sources
.end() && io
!= other
->_master_sources
.end(); ++i
, ++io
) {
1449 if ((*i
)->id() != (*io
)->id()) {
1458 Region::source_string () const
1460 //string res = itos(_sources.size());
1463 res
<< _sources
.size() << ":";
1465 SourceList::const_iterator i
;
1467 for (i
= _sources
.begin(); i
!= _sources
.end(); ++i
) {
1468 res
<< (*i
)->id() << ":";
1471 for (i
= _master_sources
.begin(); i
!= _master_sources
.end(); ++i
) {
1472 res
<< (*i
)->id() << ":";
1479 Region::uses_source (boost::shared_ptr
<const Source
> source
) const
1481 for (SourceList::const_iterator i
= _sources
.begin(); i
!= _sources
.end(); ++i
) {
1486 boost::shared_ptr
<PlaylistSource
> ps
= boost::dynamic_pointer_cast
<PlaylistSource
> (*i
);
1489 if (ps
->playlist()->uses_source (source
)) {
1499 Region::uses_source_path (const std::string
& path
) const
1501 for (SourceList::const_iterator i
= _sources
.begin(); i
!= _sources
.end(); ++i
) {
1502 boost::shared_ptr
<const FileSource
> fs
= boost::dynamic_pointer_cast
<const FileSource
> (*i
);
1504 if (fs
->path() == path
) {
1513 Region::source_length(uint32_t n
) const
1515 assert (n
< _sources
.size());
1516 return _sources
[n
]->length (_position
- _start
);
1520 Region::verify_length (framecnt_t len
)
1522 if (source() && (source()->destructive() || source()->length_mutable())) {
1526 framecnt_t maxlen
= 0;
1528 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1529 maxlen
= max (maxlen
, source_length(n
) - _start
);
1532 len
= min (len
, maxlen
);
1538 Region::verify_start_and_length (framepos_t new_start
, framecnt_t
& new_length
)
1540 if (source() && (source()->destructive() || source()->length_mutable())) {
1544 framecnt_t maxlen
= 0;
1546 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1547 maxlen
= max (maxlen
, source_length(n
) - new_start
);
1550 new_length
= min (new_length
, maxlen
);
1556 Region::verify_start (framepos_t pos
)
1558 if (source() && (source()->destructive() || source()->length_mutable())) {
1562 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1563 if (pos
> source_length(n
) - _length
) {
1571 Region::verify_start_mutable (framepos_t
& new_start
)
1573 if (source() && (source()->destructive() || source()->length_mutable())) {
1577 for (uint32_t n
= 0; n
< _sources
.size(); ++n
) {
1578 if (new_start
> source_length(n
) - _length
) {
1579 new_start
= source_length(n
) - _length
;
1585 boost::shared_ptr
<Region
>
1586 Region::get_parent() const
1588 boost::shared_ptr
<Playlist
> pl (playlist());
1591 boost::shared_ptr
<Region
> r
;
1592 boost::shared_ptr
<Region
const> grrr2
= boost::dynamic_pointer_cast
<Region
const> (shared_from_this());
1594 if (grrr2
&& (r
= _session
.find_whole_file_parent (grrr2
))) {
1595 return boost::static_pointer_cast
<Region
> (r
);
1599 return boost::shared_ptr
<Region
>();
1603 Region::apply (Filter
& filter
, Progress
* progress
)
1605 return filter
.run (shared_from_this(), progress
);
1610 Region::invalidate_transients ()
1612 _valid_transients
= false;
1613 _transients
.clear ();
1615 send_change (PropertyChange (Properties::valid_transients
));
1619 Region::drop_sources ()
1621 for (SourceList::const_iterator i
= _sources
.begin (); i
!= _sources
.end(); ++i
) {
1622 (*i
)->dec_use_count ();
1627 for (SourceList::const_iterator i
= _master_sources
.begin (); i
!= _master_sources
.end(); ++i
) {
1628 (*i
)->dec_use_count ();
1631 _master_sources
.clear ();
1635 Region::use_sources (SourceList
const & s
)
1637 set
<boost::shared_ptr
<Source
> > unique_srcs
;
1639 for (SourceList::const_iterator i
= s
.begin (); i
!= s
.end(); ++i
) {
1641 _sources
.push_back (*i
);
1642 (*i
)->inc_use_count ();
1643 _master_sources
.push_back (*i
);
1644 (*i
)->inc_use_count ();
1646 /* connect only once to DropReferences, even if sources are replicated
1649 if (unique_srcs
.find (*i
) == unique_srcs
.end ()) {
1650 unique_srcs
.insert (*i
);
1651 (*i
)->DropReferences
.connect_same_thread (*this, boost::bind (&Region::source_deleted
, this, boost::weak_ptr
<Source
>(*i
)));
1657 Region::can_trim () const
1659 CanTrim ct
= CanTrim (0);
1665 /* if not locked, we can always move the front later, and the end earlier
1668 ct
= CanTrim (ct
| FrontTrimLater
| EndTrimEarlier
);
1670 if (start() != 0 || can_trim_start_before_source_start ()) {
1671 ct
= CanTrim (ct
| FrontTrimEarlier
);
1674 if (!_sources
.empty()) {
1675 if ((start() + length()) < _sources
.front()->length (0)) {
1676 ct
= CanTrim (ct
| EndTrimLater
);
1684 Region::max_source_level () const
1688 for (SourceList::const_iterator i
= _sources
.begin(); i
!= _sources
.end(); ++i
) {
1689 lvl
= max (lvl
, (*i
)->level());
1696 Region::is_compound () const
1698 return max_source_level() > 0;