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;
76 PBD::PropertyDescriptor<framepos_t> last_relayer_bounds_from;
77 PBD::PropertyDescriptor<framepos_t> last_relayer_bounds_to;
78 PBD::PropertyDescriptor<uint64_t> last_layer_op_add;
79 PBD::PropertyDescriptor<uint64_t> last_layer_op_bounds_change;
83 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
86 Region::make_property_quarks ()
88 Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
89 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id));
90 Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
91 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id));
92 Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
93 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id));
94 Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
95 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id));
96 Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
97 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id));
98 Properties::import.property_id = g_quark_from_static_string (X_("import"));
99 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id));
100 Properties::external.property_id = g_quark_from_static_string (X_("external"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id));
102 Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id));
104 Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id));
106 Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
107 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id));
108 Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
109 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id));
110 Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
111 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id));
112 Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
113 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n", Properties::valid_transients.property_id));
114 Properties::start.property_id = g_quark_from_static_string (X_("start"));
115 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id));
116 Properties::length.property_id = g_quark_from_static_string (X_("length"));
117 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id));
118 Properties::position.property_id = g_quark_from_static_string (X_("position"));
119 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id));
120 Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
121 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id));
122 Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
123 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id));
124 Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
125 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id));
126 Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
127 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id));
128 Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
129 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id));
130 Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
131 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
132 Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
133 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
134 Properties::last_relayer_bounds_from.property_id = g_quark_from_static_string (X_("last-relayer-bounds-from"));
135 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_relayer_bounds_from = %1\n", Properties::last_relayer_bounds_from.property_id));
136 Properties::last_relayer_bounds_to.property_id = g_quark_from_static_string (X_("last-relayer-bounds-to"));
137 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_relayer_bounds_to = %1\n", Properties::last_relayer_bounds_to.property_id));
138 Properties::last_layer_op_add.property_id = g_quark_from_static_string (X_("last-layer-op-add"));
139 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_layer_op_add = %1\n", Properties::last_layer_op_add.property_id));
140 Properties::last_layer_op_bounds_change.property_id = g_quark_from_static_string (X_("last-layer-op-bounds-change"));
141 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_layer_op_bounds_change = %1\n", Properties::last_layer_op_bounds_change.property_id));
145 Region::register_properties ()
147 _xml_node_name = X_("Region");
149 add_property (_muted);
150 add_property (_opaque);
151 add_property (_locked);
152 add_property (_automatic);
153 add_property (_whole_file);
154 add_property (_import);
155 add_property (_external);
156 add_property (_sync_marked);
157 add_property (_left_of_split);
158 add_property (_right_of_split);
159 add_property (_hidden);
160 add_property (_position_locked);
161 add_property (_valid_transients);
162 add_property (_start);
163 add_property (_length);
164 add_property (_position);
165 add_property (_sync_position);
166 add_property (_layer);
167 add_property (_ancestral_start);
168 add_property (_ancestral_length);
169 add_property (_stretch);
170 add_property (_shift);
171 add_property (_position_lock_style);
172 add_property (_last_relayer_bounds_from);
173 add_property (_last_relayer_bounds_to);
174 add_property (_last_layer_op_add);
175 add_property (_last_layer_op_bounds_change);
178 #define REGION_DEFAULT_STATE(s,l) \
179 _sync_marked (Properties::sync_marked, false) \
180 , _left_of_split (Properties::left_of_split, false) \
181 , _right_of_split (Properties::right_of_split, false) \
182 , _valid_transients (Properties::valid_transients, false) \
183 , _start (Properties::start, (s)) \
184 , _length (Properties::length, (l)) \
185 , _position (Properties::position, 0) \
186 , _sync_position (Properties::sync_position, (s)) \
187 , _layer (Properties::layer, 0) \
188 , _muted (Properties::muted, false) \
189 , _opaque (Properties::opaque, true) \
190 , _locked (Properties::locked, false) \
191 , _automatic (Properties::automatic, false) \
192 , _whole_file (Properties::whole_file, false) \
193 , _import (Properties::import, false) \
194 , _external (Properties::external, false) \
195 , _hidden (Properties::hidden, false) \
196 , _position_locked (Properties::position_locked, false) \
197 , _ancestral_start (Properties::ancestral_start, (s)) \
198 , _ancestral_length (Properties::ancestral_length, (l)) \
199 , _stretch (Properties::stretch, 1.0) \
200 , _shift (Properties::shift, 1.0) \
201 , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
202 , _last_relayer_bounds_from (Properties::last_relayer_bounds_from, 0) \
203 , _last_relayer_bounds_to (Properties::last_relayer_bounds_to, 0) \
204 , _last_layer_op_add (Properties::last_layer_op_add, 0) \
205 , _last_layer_op_bounds_change (Properties::last_layer_op_bounds_change, 0)
207 #define REGION_COPY_STATE(other) \
208 _sync_marked (Properties::sync_marked, other->_sync_marked) \
209 , _left_of_split (Properties::left_of_split, other->_left_of_split) \
210 , _right_of_split (Properties::right_of_split, other->_right_of_split) \
211 , _valid_transients (Properties::valid_transients, other->_valid_transients) \
212 , _start(Properties::start, other->_start) \
213 , _length(Properties::length, other->_length) \
214 , _position(Properties::position, other->_position) \
215 , _sync_position(Properties::sync_position, other->_sync_position) \
216 , _layer (Properties::layer, other->_layer) \
217 , _muted (Properties::muted, other->_muted) \
218 , _opaque (Properties::opaque, other->_opaque) \
219 , _locked (Properties::locked, other->_locked) \
220 , _automatic (Properties::automatic, other->_automatic) \
221 , _whole_file (Properties::whole_file, other->_whole_file) \
222 , _import (Properties::import, other->_import) \
223 , _external (Properties::external, other->_external) \
224 , _hidden (Properties::hidden, other->_hidden) \
225 , _position_locked (Properties::position_locked, other->_position_locked) \
226 , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
227 , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
228 , _stretch (Properties::stretch, other->_stretch) \
229 , _shift (Properties::shift, other->_shift) \
230 , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
231 , _last_relayer_bounds_from (Properties::last_relayer_bounds_from, other->_last_relayer_bounds_from) \
232 , _last_relayer_bounds_to (Properties::last_relayer_bounds_to, other->_last_relayer_bounds_to) \
233 , _last_layer_op_add (Properties::last_layer_op_add, other->_last_layer_op_add) \
234 , _last_layer_op_bounds_change (Properties::last_layer_op_bounds_change, other->_last_layer_op_bounds_change)
236 /* derived-from-derived constructor (no sources in constructor) */
237 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
238 : SessionObject(s, name)
240 , REGION_DEFAULT_STATE(start,length)
241 , _last_length (length)
243 , _first_edit (EditChangesNothing)
245 register_properties ();
246 /* no sources at this point */
249 /** Basic Region constructor (many sources) */
250 Region::Region (const SourceList& srcs)
251 : SessionObject(srcs.front()->session(), "toBeRenamed")
252 , _type (srcs.front()->type())
253 , REGION_DEFAULT_STATE(0,0)
256 , _first_edit (EditChangesNothing)
258 register_properties ();
260 _type = srcs.front()->type();
264 assert(_sources.size() > 0);
265 assert (_type == srcs.front()->type());
268 /** Create a new Region from an existing one */
269 Region::Region (boost::shared_ptr<const Region> other)
270 : SessionObject(other->session(), other->name())
271 , _type (other->data_type())
272 , REGION_COPY_STATE (other)
273 , _last_length (other->_last_length)
274 , _last_position(other->_last_position) \
275 , _first_edit (EditChangesNothing)
277 register_properties ();
279 /* override state that may have been incorrectly inherited from the other region
287 use_sources (other->_sources);
289 _position_lock_style = other->_position_lock_style;
290 _first_edit = other->_first_edit;
292 _start = 0; // It seems strange _start is not inherited here?
294 /* sync pos is relative to start of file. our start-in-file is now zero,
295 so set our sync position to whatever the the difference between
296 _start and _sync_pos was in the other region.
298 result is that our new sync pos points to the same point in our source(s)
299 as the sync in the other region did in its source(s).
301 since we start at zero in our source(s), it is not possible to use a sync point that
302 is before the start. reset it to _start if that was true in the other region.
305 if (other->sync_marked()) {
306 if (other->_start < other->_sync_position) {
307 /* sync pos was after the start point of the other region */
308 _sync_position = other->_sync_position - other->_start;
310 /* sync pos was before the start point of the other region. not possible here. */
311 _sync_marked = false;
312 _sync_position = _start;
315 _sync_marked = false;
316 _sync_position = _start;
319 if (Profile->get_sae()) {
320 /* reset sync point to start if its ended up
321 outside region bounds.
324 if (_sync_position < _start || _sync_position >= _start + _length) {
325 _sync_marked = false;
326 _sync_position = _start;
330 assert (_type == other->data_type());
333 /** Create a new Region from part of an existing one.
335 the start within \a other is given by \a offset
336 (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
338 Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
339 : SessionObject(other->session(), other->name())
340 , _type (other->data_type())
341 , REGION_COPY_STATE (other)
342 , _last_length (other->_last_length)
343 , _last_position(other->_last_position) \
344 , _first_edit (EditChangesNothing)
346 register_properties ();
348 /* override state that may have been incorrectly inherited from the other region
356 use_sources (other->_sources);
358 _start = other->_start + offset;
360 /* if the other region had a distinct sync point
361 set, then continue to use it as best we can.
362 otherwise, reset sync point back to start.
365 if (other->sync_marked()) {
366 if (other->_sync_position < _start) {
367 _sync_marked = false;
368 _sync_position = _start;
370 _sync_position = other->_sync_position;
373 _sync_marked = false;
374 _sync_position = _start;
377 if (Profile->get_sae()) {
378 /* reset sync point to start if its ended up
379 outside region bounds.
382 if (_sync_position < _start || _sync_position >= _start + _length) {
383 _sync_marked = false;
384 _sync_position = _start;
388 assert (_type == other->data_type());
391 /** Create a copy of @param other but with different sources. Used by filters */
392 Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
393 : SessionObject (other->session(), other->name())
394 , _type (srcs.front()->type())
395 , REGION_COPY_STATE (other)
396 , _last_length (other->_last_length)
397 , _last_position (other->_last_position)
398 , _first_edit (EditChangesID)
400 register_properties ();
403 _position_locked = false;
405 other->_first_edit = EditChangesName;
407 if (other->_extra_xml) {
408 _extra_xml = new XMLNode (*other->_extra_xml);
414 assert(_sources.size() > 0);
419 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
424 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
426 _playlist = wpl.lock();
430 Region::set_name (const std::string& str)
433 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
434 assert(_name == str);
436 send_change (Properties::name);
443 Region::set_length (framecnt_t len)
445 //cerr << "Region::set_length() len = " << len << endl;
450 if (_length != len && len != 0) {
452 /* check that the current _position wouldn't make the new
456 if (max_framepos - len < _position) {
460 if (!verify_length (len)) {
465 _last_length = _length;
466 set_length_internal (len);
470 invalidate_transients ();
472 if (!property_changes_suspended()) {
476 send_change (Properties::length);
481 Region::set_length_internal (framecnt_t len)
487 Region::maybe_uncopy ()
489 /* this does nothing but marked a semantic moment once upon a time */
493 Region::first_edit ()
495 boost::shared_ptr<Playlist> pl (playlist());
497 if (_first_edit != EditChangesNothing && pl) {
499 _name = RegionFactory::new_region_name (_name);
500 _first_edit = EditChangesNothing;
502 send_change (Properties::name);
504 RegionFactory::CheckNewRegion (shared_from_this());
509 Region::at_natural_position () const
511 boost::shared_ptr<Playlist> pl (playlist());
517 boost::shared_ptr<Region> whole_file_region = get_parent();
519 if (whole_file_region) {
520 if (_position == whole_file_region->position() + _start) {
529 Region::move_to_natural_position ()
531 boost::shared_ptr<Playlist> pl (playlist());
537 boost::shared_ptr<Region> whole_file_region = get_parent();
539 if (whole_file_region) {
540 set_position (whole_file_region->position() + _start);
545 Region::special_set_position (framepos_t pos)
547 /* this is used when creating a whole file region as
548 a way to store its "natural" or "captured" position.
551 _position = _position;
556 Region::set_position_lock_style (PositionLockStyle ps)
558 if (_position_lock_style != ps) {
560 boost::shared_ptr<Playlist> pl (playlist());
562 _position_lock_style = ps;
564 if (_position_lock_style == MusicTime) {
565 _session.tempo_map().bbt_time (_position, _bbt_time);
568 send_change (Properties::position_lock_style);
573 Region::update_after_tempo_map_change ()
575 boost::shared_ptr<Playlist> pl (playlist());
577 if (!pl || _position_lock_style != MusicTime) {
581 TempoMap& map (_session.tempo_map());
582 framepos_t pos = map.frame_time (_bbt_time);
583 set_position_internal (pos, false);
585 /* do this even if the position is the same. this helps out
586 a GUI that has moved its representation already.
588 send_change (Properties::position);
592 Region::set_position (framepos_t pos)
598 set_position_internal (pos, true);
600 /* do this even if the position is the same. this helps out
601 a GUI that has moved its representation already.
603 send_change (Properties::position);
608 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
610 /* We emit a change of Properties::position even if the position hasn't changed
611 (see Region::set_position), so we must always set this up so that
612 e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
614 _last_position = _position;
616 if (_position != pos) {
619 /* check that the new _position wouldn't make the current
620 length impossible - if so, change the length.
622 XXX is this the right thing to do?
625 if (max_framepos - _length < _position) {
626 _last_length = _length;
627 _length = max_framepos - _position;
630 if (allow_bbt_recompute) {
631 recompute_position_from_lock_style ();
634 //invalidate_transients ();
639 Region::recompute_position_from_lock_style ()
641 if (_position_lock_style == MusicTime) {
642 _session.tempo_map().bbt_time (_position, _bbt_time);
647 Region::nudge_position (frameoffset_t n)
657 framepos_t new_position = _position;
660 if (_position > max_framepos - n) {
661 new_position = max_framepos;
666 if (_position < -n) {
673 set_position_internal (new_position, true);
675 send_change (Properties::position);
679 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
681 _ancestral_length = l;
682 _ancestral_start = s;
688 Region::set_start (framepos_t pos)
690 if (locked() || position_locked()) {
693 /* This just sets the start, nothing else. It effectively shifts
694 the contents of the Region within the overall extent of the Source,
695 without changing the Region's position or length
700 if (!verify_start (pos)) {
707 invalidate_transients ();
709 send_change (Properties::start);
714 Region::trim_start (framepos_t new_position)
716 if (locked() || position_locked()) {
719 framepos_t new_start;
720 frameoffset_t const start_shift = new_position - _position;
722 if (start_shift > 0) {
724 if (_start > max_framepos - start_shift) {
725 new_start = max_framepos;
727 new_start = _start + start_shift;
730 if (!verify_start (new_start)) {
734 } else if (start_shift < 0) {
736 if (_start < -start_shift) {
739 new_start = _start + start_shift;
746 if (new_start == _start) {
754 send_change (Properties::start);
758 Region::trim_front (framepos_t new_position)
760 modify_front (new_position, false);
764 Region::cut_front (framepos_t new_position)
766 modify_front (new_position, true);
770 Region::cut_end (framepos_t new_endpoint)
772 modify_end (new_endpoint, true);
776 Region::modify_front (framepos_t new_position, bool reset_fade)
782 framepos_t end = last_frame();
783 framepos_t source_zero;
785 if (_position > _start) {
786 source_zero = _position - _start;
788 source_zero = 0; // its actually negative, but this will work for us
791 if (new_position < end) { /* can't trim it zero or negative length */
793 framecnt_t newlen = 0;
794 framepos_t delta = 0;
796 if (!can_trim_start_before_source_start ()) {
797 /* can't trim it back past where source position zero is located */
798 new_position = max (new_position, source_zero);
801 if (new_position > _position) {
802 newlen = _length - (new_position - _position);
803 delta = -1 * (new_position - _position);
805 newlen = _length + (_position - new_position);
806 delta = _position - new_position;
809 trim_to_internal (new_position, newlen);
812 _right_of_split = true;
815 if (!property_changes_suspended()) {
816 recompute_at_start ();
819 if (_transients.size() > 0){
820 adjust_transients(delta);
826 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
832 if (new_endpoint > _position) {
833 trim_to_internal (_position, new_endpoint - _position);
835 _left_of_split = true;
837 if (!property_changes_suspended()) {
843 /** @param new_endpoint New region end point, such that, for example,
844 * a region at 0 of length 10 has an endpoint of 9.
848 Region::trim_end (framepos_t new_endpoint)
850 modify_end (new_endpoint, false);
854 Region::trim_to (framepos_t position, framecnt_t length)
860 trim_to_internal (position, length);
862 if (!property_changes_suspended()) {
863 recompute_at_start ();
869 Region::trim_to_internal (framepos_t position, framecnt_t length)
871 framepos_t new_start;
877 frameoffset_t const start_shift = position - _position;
879 if (start_shift > 0) {
881 if (_start > max_framepos - start_shift) {
882 new_start = max_framepos;
884 new_start = _start + start_shift;
887 } else if (start_shift < 0) {
889 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
892 new_start = _start + start_shift;
899 if (!verify_start_and_length (new_start, length)) {
903 PropertyChange what_changed;
905 if (_start != new_start) {
907 what_changed.add (Properties::start);
910 /* Set position before length, otherwise for MIDI regions this bad thing happens:
911 * 1. we call set_length_internal; length in beats is computed using the region's current
912 * (soon-to-be old) position
913 * 2. we call set_position_internal; position is set and length in frames re-computed using
914 * length in beats from (1) but at the new position, which is wrong if the region
915 * straddles a tempo/meter change.
918 if (_position != position) {
919 if (!property_changes_suspended()) {
920 _last_position = _position;
922 set_position_internal (position, true);
923 what_changed.add (Properties::position);
926 if (_length != length) {
927 if (!property_changes_suspended()) {
928 _last_length = _length;
930 set_length_internal (length);
931 what_changed.add (Properties::length);
936 PropertyChange start_and_length;
938 start_and_length.add (Properties::start);
939 start_and_length.add (Properties::length);
941 if (what_changed.contains (start_and_length)) {
945 if (!what_changed.empty()) {
946 send_change (what_changed);
951 Region::set_hidden (bool yn)
953 if (hidden() != yn) {
955 send_change (Properties::hidden);
960 Region::set_whole_file (bool yn)
963 /* no change signal */
967 Region::set_automatic (bool yn)
970 /* no change signal */
974 Region::set_muted (bool yn)
978 send_change (Properties::muted);
983 Region::set_opaque (bool yn)
985 if (opaque() != yn) {
987 send_change (Properties::opaque);
992 Region::set_locked (bool yn)
994 if (locked() != yn) {
996 send_change (Properties::locked);
1001 Region::set_position_locked (bool yn)
1003 if (position_locked() != yn) {
1004 _position_locked = yn;
1005 send_change (Properties::locked);
1009 /** Set the region's sync point.
1010 * @param absolute_pos Session time.
1013 Region::set_sync_position (framepos_t absolute_pos)
1015 /* position within our file */
1016 framepos_t const file_pos = _start + (absolute_pos - _position);
1018 if (file_pos != _sync_position) {
1019 _sync_marked = true;
1020 _sync_position = file_pos;
1021 if (!property_changes_suspended()) {
1025 send_change (Properties::sync_position);
1030 Region::clear_sync_position ()
1032 if (sync_marked()) {
1033 _sync_marked = false;
1034 if (!property_changes_suspended()) {
1038 send_change (Properties::sync_position);
1042 /* @return the sync point relative the first frame of the region */
1044 Region::sync_offset (int& dir) const
1046 if (sync_marked()) {
1047 if (_sync_position > _start) {
1049 return _sync_position - _start;
1052 return _start - _sync_position;
1061 Region::adjust_to_sync (framepos_t pos) const
1064 frameoffset_t offset = sync_offset (sync_dir);
1066 // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1075 if (max_framepos - pos > offset) {
1083 /** @return Sync position in session time */
1085 Region::sync_position() const
1087 if (sync_marked()) {
1088 return _position - _start + _sync_position;
1090 /* if sync has not been marked, use the start of the region */
1098 boost::shared_ptr<Playlist> pl (playlist());
1100 pl->raise_region (shared_from_this ());
1107 boost::shared_ptr<Playlist> pl (playlist());
1109 pl->lower_region (shared_from_this ());
1115 Region::raise_to_top ()
1117 boost::shared_ptr<Playlist> pl (playlist());
1119 pl->raise_region_to_top (shared_from_this());
1124 Region::lower_to_bottom ()
1126 boost::shared_ptr<Playlist> pl (playlist());
1128 pl->lower_region_to_bottom (shared_from_this());
1133 Region::set_layer (layer_t l)
1137 send_change (Properties::layer);
1140 Evoral::Range<framepos_t> const b = bounds ();
1141 _last_relayer_bounds_from = b.from;
1142 _last_relayer_bounds_to = b.to;
1148 XMLNode *node = new XMLNode ("Region");
1151 LocaleGuard lg (X_("POSIX"));
1152 const char* fe = NULL;
1154 add_properties (*node);
1156 id().print (buf, sizeof (buf));
1157 node->add_property ("id", buf);
1158 node->add_property ("type", _type.to_string());
1160 switch (_first_edit) {
1161 case EditChangesNothing:
1164 case EditChangesName:
1170 default: /* should be unreachable but makes g++ happy */
1175 node->add_property ("first-edit", fe);
1177 /* note: flags are stored by derived classes */
1179 if (_position_lock_style != AudioTime) {
1182 node->add_property ("bbt-position", str.str());
1185 for (uint32_t n=0; n < _sources.size(); ++n) {
1186 snprintf (buf2, sizeof(buf2), "source-%d", n);
1187 _sources[n]->id().print (buf, sizeof(buf));
1188 node->add_property (buf2, buf);
1191 for (uint32_t n=0; n < _master_sources.size(); ++n) {
1192 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1193 _master_sources[n]->id().print (buf, sizeof (buf));
1194 node->add_property (buf2, buf);
1197 /* Only store nested sources for the whole-file region that acts
1198 as the parent/root of all regions using it.
1201 if (_whole_file && max_source_level() > 0) {
1203 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1205 /* region is compound - get its playlist and
1206 store that before we list the region that
1210 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1211 nested_node->add_child_nocopy ((*s)->get_state ());
1215 node->add_child_nocopy (*nested_node);
1220 node->add_child_copy (*_extra_xml);
1227 Region::get_state ()
1233 Region::set_state (const XMLNode& node, int version)
1235 PropertyChange what_changed;
1236 return _set_state (node, version, what_changed, true);
1240 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1242 const XMLProperty* prop;
1244 Stateful::save_extra_xml (node);
1246 what_changed = set_values (node);
1250 if (_position_lock_style == MusicTime) {
1251 if ((prop = node.property ("bbt-position")) == 0) {
1252 /* missing BBT info, revert to audio time locking */
1253 _position_lock_style = AudioTime;
1255 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1258 &_bbt_time.ticks) != 3) {
1259 _position_lock_style = AudioTime;
1264 /* fix problems with old sessions corrupted by impossible
1265 values for _stretch or _shift
1267 if (_stretch == 0.0f) {
1271 if (_shift == 0.0f) {
1276 send_change (what_changed);
1279 /* Quick fix for 2.x sessions when region is muted */
1280 if ((prop = node.property (X_("flags")))) {
1281 if (string::npos != prop->value().find("Muted")){
1291 Region::suspend_property_changes ()
1293 Stateful::suspend_property_changes ();
1294 _last_length = _length;
1295 _last_position = _position;
1299 Region::mid_thaw (const PropertyChange& what_changed)
1301 if (what_changed.contains (Properties::length)) {
1302 if (what_changed.contains (Properties::position)) {
1303 recompute_at_start ();
1305 recompute_at_end ();
1310 Region::send_change (const PropertyChange& what_changed)
1312 if (what_changed.empty()) {
1316 Stateful::send_change (what_changed);
1318 if (!Stateful::property_changes_suspended()) {
1320 /* Try and send a shared_pointer unless this is part of the constructor.
1325 boost::shared_ptr<Region> rptr = shared_from_this();
1326 RegionPropertyChanged (rptr, what_changed);
1328 /* no shared_ptr available, relax; */
1334 Region::set_last_layer_op (LayerOp op, uint64_t when)
1338 _last_layer_op_add = when;
1340 case LayerOpBoundsChange:
1341 _last_layer_op_bounds_change = when;
1347 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1349 return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1353 Region::equivalent (boost::shared_ptr<const Region> other) const
1355 return _start == other->_start &&
1356 _position == other->_position &&
1357 _length == other->_length;
1361 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1363 return _start == other->_start &&
1364 _length == other->_length;
1368 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1370 return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1374 Region::source_deleted (boost::weak_ptr<Source>)
1378 if (!_session.deletion_in_progress()) {
1379 /* this is a very special case: at least one of the region's
1380 sources has bee deleted, so invalidate all references to
1381 ourselves. Do NOT do this during session deletion, because
1382 then we run the risk that this will actually result
1383 in this object being deleted (as refcnt goes to zero)
1384 while emitting DropReferences.
1392 Region::master_source_names ()
1394 SourceList::iterator i;
1396 vector<string> names;
1397 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1398 names.push_back((*i)->name());
1405 Region::set_master_sources (const SourceList& srcs)
1407 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1408 (*i)->dec_use_count ();
1411 _master_sources = srcs;
1412 assert (_sources.size() == _master_sources.size());
1414 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1415 (*i)->inc_use_count ();
1420 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1425 if ((_sources.size() != other->_sources.size()) ||
1426 (_master_sources.size() != other->_master_sources.size())) {
1430 SourceList::const_iterator i;
1431 SourceList::const_iterator io;
1433 for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1434 if ((*i)->id() != (*io)->id()) {
1439 for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1440 if ((*i)->id() != (*io)->id()) {
1449 Region::source_string () const
1451 //string res = itos(_sources.size());
1454 res << _sources.size() << ":";
1456 SourceList::const_iterator i;
1458 for (i = _sources.begin(); i != _sources.end(); ++i) {
1459 res << (*i)->id() << ":";
1462 for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1463 res << (*i)->id() << ":";
1470 Region::uses_source (boost::shared_ptr<const Source> source) const
1472 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1477 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1480 if (ps->playlist()->uses_source (source)) {
1490 Region::source_length(uint32_t n) const
1492 assert (n < _sources.size());
1493 return _sources[n]->length (_position - _start);
1497 Region::verify_length (framecnt_t len)
1499 if (source() && (source()->destructive() || source()->length_mutable())) {
1503 framecnt_t maxlen = 0;
1505 for (uint32_t n = 0; n < _sources.size(); ++n) {
1506 maxlen = max (maxlen, source_length(n) - _start);
1509 len = min (len, maxlen);
1515 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1517 if (source() && (source()->destructive() || source()->length_mutable())) {
1521 framecnt_t maxlen = 0;
1523 for (uint32_t n = 0; n < _sources.size(); ++n) {
1524 maxlen = max (maxlen, source_length(n) - new_start);
1527 new_length = min (new_length, maxlen);
1533 Region::verify_start (framepos_t pos)
1535 if (source() && (source()->destructive() || source()->length_mutable())) {
1539 for (uint32_t n = 0; n < _sources.size(); ++n) {
1540 if (pos > source_length(n) - _length) {
1548 Region::verify_start_mutable (framepos_t& new_start)
1550 if (source() && (source()->destructive() || source()->length_mutable())) {
1554 for (uint32_t n = 0; n < _sources.size(); ++n) {
1555 if (new_start > source_length(n) - _length) {
1556 new_start = source_length(n) - _length;
1562 boost::shared_ptr<Region>
1563 Region::get_parent() const
1565 boost::shared_ptr<Playlist> pl (playlist());
1568 boost::shared_ptr<Region> r;
1569 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1571 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1572 return boost::static_pointer_cast<Region> (r);
1576 return boost::shared_ptr<Region>();
1580 Region::apply (Filter& filter, Progress* progress)
1582 return filter.run (shared_from_this(), progress);
1587 Region::invalidate_transients ()
1589 _valid_transients = false;
1590 _transients.clear ();
1592 send_change (PropertyChange (Properties::valid_transients));
1596 Region::drop_sources ()
1598 for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1599 (*i)->dec_use_count ();
1604 for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1605 (*i)->dec_use_count ();
1608 _master_sources.clear ();
1612 Region::use_sources (SourceList const & s)
1614 set<boost::shared_ptr<Source> > unique_srcs;
1616 for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1618 _sources.push_back (*i);
1619 (*i)->inc_use_count ();
1620 _master_sources.push_back (*i);
1621 (*i)->inc_use_count ();
1623 /* connect only once to DropReferences, even if sources are replicated
1626 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1627 unique_srcs.insert (*i);
1628 (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1634 Region::can_trim () const
1636 CanTrim ct = CanTrim (0);
1642 /* if not locked, we can always move the front later, and the end earlier
1645 ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1647 if (start() != 0 || can_trim_start_before_source_start ()) {
1648 ct = CanTrim (ct | FrontTrimEarlier);
1651 if (!_sources.empty()) {
1652 if ((start() + length()) < _sources.front()->length (0)) {
1653 ct = CanTrim (ct | EndTrimLater);
1661 Region::max_source_level () const
1665 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1666 lvl = max (lvl, (*i)->level());
1673 Region::is_compound () const
1675 return max_source_level() > 0;
1679 Region::post_set (const PropertyChange& pc)
1681 if (pc.contains (Properties::position)) {
1682 recompute_position_from_lock_style ();
1687 Region::last_layer_op (LayerOp op) const
1691 return _last_layer_op_add;
1692 case LayerOpBoundsChange:
1693 return _last_layer_op_bounds_change;
1700 Evoral::Range<framepos_t>
1701 Region::bounds () const
1703 return Evoral::Range<framepos_t> (_position, _position + _length);